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/pmdas/hotproc | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/hotproc')
30 files changed, 4273 insertions, 0 deletions
diff --git a/src/pmdas/hotproc/GNUakefile b/src/pmdas/hotproc/GNUakefile new file mode 100644 index 0000000..7ebb155 --- /dev/null +++ b/src/pmdas/hotproc/GNUakefile @@ -0,0 +1,52 @@ +#!make + +LTARGETS = help.dir +LLDIRT = domain.h *.log *.dir *.pag pmns help + +PCP_SRC_DEPTH = ../.. +include $(PCP_SRC_DEPTH)/include/commondefs +include $(PCP_SRC_DEPTH)/include/isacommondefs + +PROC_DIR = ../proc +IAM = hotproc +DOMAIN = HOTPROC +IDBTAG = PMDA_$(DOMAIN) +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +install: default + $(INSTALL) -F /usr/pcp/lib -idb "$(IDBTAG)" -lns ../../..$(PMDADIR)/pmda$(IAM) pmda$(IAM) + $(INSTALL) -idb '$(IDBTAG) removeop("rm -f $$rbase$(PMDADIR)/help.*")' -m 755 -dir $(PMDADIR) + $(INSTALL) -F /usr/pcp/pmdas -idb "$(IDBTAG)" -lns ../../..$(PMDADIR) $(IAM) +#if $(BEFORE_IRIX6_5) + $(I_32) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE32) $(STRIPBIN)" -m 555 -src 32/pmda$(IAM) pmda$(IAM) + $(I_64) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE64) $(STRIPBIN)" -m 555 -src 64/pmda$(IAM) pmda$(IAM) +#else + $(I_N32) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE32) $(STRIPBIN)" -m 555 -src N32/pmda$(IAM) pmda$(IAM) + $(I_64) $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG) $(MODE64) $(STRIPBIN)" -m 555 -src 64/pmda$(IAM) pmda$(IAM) +#endif + $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG)" -m 555 Install Remove + $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG)" -m 444 README root help pmns domain.h sample.conf general.conf general.pmie + $(INSTALL) -f $(PMDADIR) -idb "$(IDBTAG)" -m 444 -src Makefile.install Makefile + +help: $(PROC_DIR)/help help.hotproc pmns + sed < $(PROC_DIR)/help -e 's/proc\./hotproc./g' \ + -e 's/number of processes/number of "interesting" processes/g' \ + | cat - help.hotproc | ./help.fmt > $@ + +help.dir: domain.h help root pmns ../../buildtools/newhelp + PCP_SRC_DEPTH=$(PCP_SRC_DEPTH) $(PCP_SRC_DEPTH)/buildtools/check_help_src help root + +pmns: $(PROC_DIR)/root_proc pmns.hotproc fixpmns.awk + nawk < $(PROC_DIR)/root_proc -f fixpmns.awk \ + | sed -e '/[ ]PROC:/s/PROC:/HOTPROC:/g' -e 's/^proc/hotproc/g' \ + | cat - pmns.hotproc >$@ + +.NOTPARALLEL: +.ORDER: domain.h $(OBJECTS) + +domain.h: ../../pmns/stdpmid + rm -f domain.h + echo "/*" >domain.h + echo " * built from $(PCP_VAR_DIR)/pmns/stdpmid" >>domain.h + echo " */" >>domain.h + nawk <../../pmns/stdpmid >>domain.h '/#define[ ][ ]*$(DOMAIN)[ ]/ { print "#define $(DOMAIN) " $$3 }' diff --git a/src/pmdas/hotproc/GNUmakefile b/src/pmdas/hotproc/GNUmakefile new file mode 100644 index 0000000..2c69ac6 --- /dev/null +++ b/src/pmdas/hotproc/GNUmakefile @@ -0,0 +1,157 @@ +# +# Copyright (c) 1995-2002,2007 Silicon Graphics, Inc. All Rights Reserved. +# +# 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 + +# This is just to get the source exported to the community. +# 'Makefile' in particular is included only for clues it might offer, +# and should almost certainly be removed from SRCFILES once +# the pmda is working. +LSRCFILES = fixpmns.awk general.pmie help.fmt Install \ + pmns.hotproc Remove sample.conf \ + general.conf help.hotproc Makefile README root +SUBDIRS = src + +default default_pcp: +install install_pcp: +include $(BUILDRULES) + +# Remove # from start of all following lines to get pre-existing +# (but currently non-functional) content + +#IAM = hotproc +#DOMAIN = HOTPROC +# +#TARGETS = $(IAM) pmns help +# +#PROC_DIR = ../linux +#PROC_SRCDIR = $(PROC_DIR)/src +# +#FROM_PROC_H = cluster.h pracinfo.h proc_aux.h pscred.h \ +# pstatus.h pmemory.h proc.h procmem.h psinfo.h \ +# psusage.h ctltab.h nameinfo.h +#FROM_PROC_C = pmemory.c pracinfo.c proc_aux.c \ +# pscred.c psinfo.c pstatus.c psusage.c \ +# ttyname.c procmem.c nameinfo.c +# +#FROM_PROC = $(FROM_PROC_C) $(FROM_PROC_H) +# +#CFILES = $(FROM_PROC_C) \ +# pglobal.c ctltab.c hotproc.c pcpu.c \ +# config.c gram_node.c error.c ppred_values.c +# +#OBJECTS = gram.o lex.o $(CFILES:S/.c/.o/g) +# +#LCOPTS = -fullwarn +#LCINCS = $(PCP_INC_PATH) +#LCDEFS = $(DEBUG) +#LLDOPTS = $(PCP_LIBS_PATH) +#LLDLIBS = $(PCP_PMDALIB) +# +#PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +#LDIRT = domain.h *.log *.dir *.pag +# +#default: $(TARGETS) check_help_src +# +#include $(TOPDIR)/src/include/buildrules.pro +# +#install: default +# $(INSTALL) -m 755 -d $(PMDADIR) +# $(INSTALL) -lns ../../..$(PMDADIR)/pmda$(IAM) pmda$(IAM) $(PCP_SHARE_DIR)/lib +# $(INSTALL) -lns ../../..$(PMDADIR) $(IAM) $(PCP_PMDAS_DIR) +# $(INSTALL) -m 755 pmda$(IAM) $(PMDADIR) +# $(INSTALL) -m 755 Install Remove $(PMDADIR) +# $(INSTALL) -m 644 README root help pmns domain.h sample.conf general.conf general.pmie $(PMDADIR) +# +#$(IAM): $(OBJECTS) +# +#domain.h: ../../pmns/stdpmid +# $(DOMAIN_MAKERULE) +# +#help: $(PROC_DIR)/help help.hotproc pmns +# $(AWK) <$(PROC_DIR)/help '\ +#$1 == "@" { want=0 }\ +#$1 == "@" && $2 ~ /^proc/ { want=1 }\ +#want == 1 { print }' \ +# | sed -e 's/proc\./hotproc./g' \ +# -e 's/number of processes/number of "interesting" processes/g' \ +# | cat - help.hotproc \ +# | ./help.fmt >$@ +# +#pmns: $(PROC_DIR)/root_linux pmns.hotproc fixpmns.awk +# $(AWK) < $(PROC_DIR)/root_linux -f fixpmns.awk \ +# | sed -e '/nprocs/d' -e 's/60:/HOTPROC:/g' -e 's/^proc/hotproc/g' \ +# | cat - pmns.hotproc >$@ +# +#hotproc.o: domain.h +# +#config.o: gram.tab.h +# +#check_help_src: domain.h help root pmns +# PCP_SRC_DEPTH=$(PCP_SRC_DEPTH) $(PCP_SRC_DEPTH)/buildtools/check_help_src help root +# +## +## PROC_SRCDIR dependencies +## +#cluster.h: $(PROC_SRCDIR)/cluster.h +# ln -s $? $@ +#pracinfo.h: $(PROC_SRCDIR)/pracinfo.h +# ln -s $? $@ +#pracinfo.c: $(PROC_SRCDIR)/pracinfo.c +# ln -s $? $@ +#proc_aux.h: $(PROC_SRCDIR)/proc_aux.h +# ln -s $? $@ +#proc_aux.c: $(PROC_SRCDIR)/proc_aux.c +# ln -s $? $@ +#pscred.h: $(PROC_SRCDIR)/pscred.h +# ln -s $? $@ +#pscred.c: $(PROC_SRCDIR)/pscred.c +# ln -s $? $@ +#pstatus.h: $(PROC_SRCDIR)/pstatus.h +# ln -s $? $@ +#pstatus.c: $(PROC_SRCDIR)/pstatus.c +# ln -s $? $@ +#pmemory.h: $(PROC_SRCDIR)/pmemory.h +# ln -s $? $@ +#pmemory.c: $(PROC_SRCDIR)/pmemory.c +# ln -s $? $@ +#proc.h: $(PROC_SRCDIR)/proc.h +# ln -s $? $@ +#procmem.h: $(PROC_SRCDIR)/procmem.h +# ln -s $? $@ +#procmem.c: $(PROC_SRCDIR)/procmem.c +# ln -s $? $@ +#psinfo.h: $(PROC_SRCDIR)/psinfo.h +# ln -s $? $@ +#psinfo.c: $(PROC_SRCDIR)/psinfo.c +# ln -s $? $@ +#psusage.h: $(PROC_SRCDIR)/psusage.h +# ln -s $? $@ +#psusage.c: $(PROC_SRCDIR)/psusage.c +# ln -s $? $@ +#ttyname.c: $(PROC_SRCDIR)/ttyname.c +# ln -s $? $@ +#ctltab.h: $(PROC_SRCDIR)/ctltab.h +# ln -s $? $@ +#nameinfo.h: $(PROC_SRCDIR)/nameinfo.h +# ln -s $? $@ +#nameinfo.c: $(PROC_SRCDIR)/nameinfo.c +# ln -s $? $@ +# +# +# +# +#default_pro : default +# diff --git a/src/pmdas/hotproc/Install b/src/pmdas/hotproc/Install new file mode 100644 index 0000000..e1cc8b1 --- /dev/null +++ b/src/pmdas/hotproc/Install @@ -0,0 +1,150 @@ +#!/bin/sh +# +# Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# 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. +# +# Install the hotproc PMDA and/or PMNS +# +# XXX these values are overwritten by pmdaproc.sh! +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +status=1 # failure is the default! +trap "cd /; rm -rf $tmp; exit \$status" 0 1 2 3 15 + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=hotproc +pmda_interface=2 +forced_restart=false + +pmdaSetup + +daemon_opt=true # can install as daemon +dso_opt=false +pipe_opt=true # supports pipe IPC +socket_opt=false # no socket IPC + +# be careful that mortals cannot write any configuration files, as +# these would present a security problem +# +umask 022 + +# PMDA variables +# +configfile="" +refresh=60 +debug=0 +storable="n" + +do_debug=false + +_parsedefaults() +{ + echo "Extracting options from current installation ..." + while getopts D:d:h:i:l:p:st:u: c + do + case $c in + \?) echo "Warning: Unrecognized option in $PCP_PMCDCONF_PATH" + echo " Remove line for the $iam PMDA in $PCP_PMCDCONF_PATH and re-run ./Install" + exit 2;; + D ) debug=$OPTARG;; + t ) refresh=$OPTARG;; + s ) storable="n";; + * ) ;; + esac + done + eval configfile='$'$OPTIND +} + +if $do_pmda +then + + # set options from $PCP_PMCDCONF_PATH, if possible + # + ans=`$PCP_AWK_PROG <$PCP_PMCDCONF_PATH ' +$1 == "'$iam'" { printf "%s",$6 + for (i=7;i<=NF;i++) printf " %s",$i + print "" + }'` + if [ ! -z "$ans" ] + then + _parsedefaults $ans + fi + + # go figure out which configuration file to use ... + # + default_configfile=./sample.conf + pmdaChooseConfigFile + if [ -z "$configfile" ] + then + echo "" + echo "Error: Abandoning installation as no configuration file was specified." + exit 1 + fi + + # make sure that the chosen configuration file parses ok + # + $pmda_dir/$pmda_name -C $configfile >$tmp/err 2>&1 + if [ $? -eq 1 ] + then + echo "" + echo "Error: Abandoning installation due to errors in configuration file:" + cat $tmp/err + exit 1 + fi + + echo + echo "Enter the refresh interval (in seconds) [$refresh] \c" + read ans + if [ ! -z "$ans" ] + then + refresh=$ans + fi + + echo + echo "Do you want to modify the configuration predicate or refresh interval" + echo "at run time [$storable]? \c" + read ans + if [ ! -z "$ans" ] + then + case $ans in + N|n|NO|No|no) storable=n;; + Y|y|YES|Yes|yes) storable=y;; + esac + fi + if [ $storable = y ] + then + store_opt="" + else + store_opt="-s" + fi + + if [ "$do_debug" = true ] + then + echo + echo "Enter the debugging flag (see pmdbg(1)) [$debug] \c" + read ans + if [ ! -z "$ans" ] + then + debug=$ans + fi + fi + + args="$store_opt -t $refresh -D $debug $configfile" +fi + +pmdaInstall +echo "Please note that instance related metrics will not be available" +echo "until after the initial refresh of $refresh seconds." + +status=0 +exit 0 diff --git a/src/pmdas/hotproc/README b/src/pmdas/hotproc/README new file mode 100644 index 0000000..be8c5a4 --- /dev/null +++ b/src/pmdas/hotproc/README @@ -0,0 +1,141 @@ +Performance Co-Pilot hotproc PMDA for Active Process Monitoring +=============================================================== + +This PMDA is designed to be configurable to monitor processes which +the administrator deems "hot" or "interesting." The PMDA is similar +to the proc PMDA except in two main aspects: + +(i) it extends the proc metric set by: + hotproc.cpuburn, + hotproc.control.*, + hotproc.predicate.*, + hotproc.total.* . + +(ii) it allows one to retrieve all the instances. + +It is allowed to retrieve all the instances because the set of +instances is restricted by a predicate specified in a configuration +file. The predicate specifies what processes are "interesting", for +example, + + (cpuburn > 0.1 && uname == "root") + +and it applies this predicate every <refresh> seconds. + +Therefore, hotproc.nprocs now refers to the number of "interesting" +processes instead of the list of all the processes. + +To monitor how successful (according to activity) that the +configuration predicate and refresh interval are, the hotproc.total.* +metrics can be used. For example, hotproc.total.cpuother.transient +shows how much of the cpu that transient processes (ones which do not +live for the refresh interval) get. If one is interested in some of +these processes then reducing the refresh interval may catch them. +Hotproc.total.cpuother.not_cpuburn indicates how much of the cpu that +the "uninteresting" processes are getting. On the basis of this value, +one may decide to change what is "interesting" by modifying the +configuration predicate. If one wants to get a simple indication of how +much of the cpu that all of the transient and "uninteresting" processes +are getting, then hotproc.total.cpuother.percent is the answer. + +In order to see why the instances (processes) of the hotproc agent +were chosen, one can check the hotproc.predicate.* metrics. These +metrics show the values used by the predicate evaluation at the +last refresh of the instance domain. For example, if one used a +predicate of (syscalls > 100), then doing: + $ pminfo -f hotproc.predicate.syscalls +will show the values of the system call rates of the processes +which satisfy the predicate (i.e. are greater than 100 per second +over the last refresh interval). + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT hotproc + +Installation of the hotproc PMDA +================================ + + + # cd $PCP_PMDAS_DIR/hotproc + + + Check that there is no clash with the Performance Metrics Domain + number defined in domain.h and the other PMDAs currently in use + (see $PCP_PMCDCONF_PATH). If there is, edit domain.h and choose + another domain number. + + + Inspect the ./sample.conf file and either modify it or create a new + configuration file that suits your need for "interesting". See + pmdahotproc(1) for configuration specification. + + + Then run the Install script (as root) + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + Answer the questions, which include the option to specify the + configuration file that you created. You will also need to specify + a refresh interval which determines how often the "hot" predicate + is used over the current set of processes. A smaller number will + mean that the predicate will be able to choose processes which have + short lives or sporadic activity but will consume more CPU because + it is run more often. + + + At the end of installation a check is made to verify that the + metrics of the agent can be retrieved. The reported number from this + check will be low because most of the hotproc metrics will not be + available until after the first refresh interval. + +Special TRIX Installation Considerations +======================================== + + For SGI Trix systems, the hotproc PMDA needs the CAP_MAC_READ + capability in addition to the default capability (CAP_SCHED_MGT), + before it can interrogate the resource utilization of all processes. + + To achieve this, run the ./Install script as described above, then + + 1. edit /etc/pmcd.conf and for the pmdahotproc line, replace the + pmda invocation arguments + $PCP_PMDAS_DIR/hotproc/pmdahotproc ... + by + /sbin/suattr -C CAP_SCHED_MGT,CAP_MAC_READ+ipe -c "$PCP_PMDAS_DIR/hotproc/pmdahotproc ..." + + 2. restart pmcd + # /etc/init.d/pcp start + + Thanks to Roald Lygre for this recipe. + +De-installation +=============== + +Simply use + + # cd $PCP_PMDAS_DIR/hotproc + # ./Remove + +Changing the settings +===================== + +The refresh period can be dynamically modified using +pmstore(1) for the metric hotproc.control.refresh. + +To make permanent changes, re-run the Install script. + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/hotproc.log) should be checked for any warnings + or errors. + + + If the Install script reports some warnings when checking the + metrics, the problem should be listed in one of the log files. diff --git a/src/pmdas/hotproc/Remove b/src/pmdas/hotproc/Remove new file mode 100644 index 0000000..37b3a89 --- /dev/null +++ b/src/pmdas/hotproc/Remove @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. +# +# 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the sample PMDA +# + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=hotproc + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/hotproc/fixpmns.awk b/src/pmdas/hotproc/fixpmns.awk new file mode 100644 index 0000000..bf2d568 --- /dev/null +++ b/src/pmdas/hotproc/fixpmns.awk @@ -0,0 +1,35 @@ +# states +# 0 nothing interesting is happening, looking for proc { ... } +# 1 inside proc { ... }; want to copy all lines except +# "nprocs" metric that does not come from here for the +# hotproc PMDA +# 2 looking for proc.foo { ... } +# 3 inside proc.foo { ... }; want to copy all lines +# +BEGIN { print "/*" + print " * Hotproc Performance Metric Domain (PMD) Identifiers" + print " * (generated from pmns for hotproc and proc metrics)" + print " */" + print "" + print "hotproc {" + print " nprocs HOTPROC:100:0" + print " cpuburn HOTPROC:102:0" + print " total" + print " predicate" + print " control" + state = 0 + } + +state == 0 && /^proc / { state = 1; next } + +state == 1 && /nprocs/ { next } + +state == 1 && /^}/ { print; print ""; state = 2; next } + +state == 1 { print } + +state == 2 && /^proc\./ { state = 3 } + +state == 3 && /^}/ { print; print ""; state = 2; next } + +state == 3 { print } diff --git a/src/pmdas/hotproc/general.conf b/src/pmdas/hotproc/general.conf new file mode 100644 index 0000000..b51ca11 --- /dev/null +++ b/src/pmdas/hotproc/general.conf @@ -0,0 +1,27 @@ +#pmdahotproc +Version 1.0 + +# this configuration is of a more general nature, and identifies +# a number of "interesting" classes of processes ... it is assumed +# that some other monitoring tool, like pmie, would be able to +# decide which processes are members of each "interesting" class +# +# a sample pmie ruleset matching this hotproc configuration may +# be found in $PCP_PMDAS_DIR/hotproc/general.pmie +# + + cpuburn > 0.95 # one process using more than the equivalent of + # 95% of one processor + +|| iodemand > 500 # or more than 500 Kbytes/sec passing across the + # read() and write() system call interfaces + +|| syscalls > 100 # or more than 100 system calls/sec + +|| iowait > 0.33 # or more than 33% of the time is waiting for + # some sort of I/O + +|| schedwait > 0.25 # or more than 25% of the time is on the run + # queue waiting for the scheduler to assign + # a processor + diff --git a/src/pmdas/hotproc/general.pmie b/src/pmdas/hotproc/general.pmie new file mode 100755 index 0000000..6f4929c --- /dev/null +++ b/src/pmdas/hotproc/general.pmie @@ -0,0 +1,29 @@ +// this pmie configuration files assumes the hotproc PMDA has been +// configured with the "general.conf" configuration file, and thus +// is using as predicate something like: +// +// cpuburn > 0.95 || iodemand > 500 || syscalls > 100 +// || iowait > 0.33 || schedwait > 0.25 +// + +delta = 60sec; // change this if the hotproc PMDA refresh interval + // is different to the default of 60 seconds + +some_inst 100 * hotproc.psinfo.time > 95 + -> print "\nCPU: %v% busy %i"; + +io_bytes = "hotproc.accounting.counts"; +some_inst + (($io_bytes.chr + $io_bytes.chw) / 1024) > 500 + -> print "\nI/O: %v Kbyte/sec %i"; + +some_inst hotproc.psusage.sysc > 100 + -> print "\nsyscall: %v call/sec %i"; + +io_timers = "hotproc.accounting.timers"; +some_inst + 100 * ( $io_timers.bwtime + $io_timers.rwtime ) > 33 + -> print "\nI/O: %v% waiting %i"; + +some_inst 100 * $io_timers.qwtime > 25 + -> print "\nCPU: %v% runq waiting %i"; diff --git a/src/pmdas/hotproc/help.fmt b/src/pmdas/hotproc/help.fmt new file mode 100755 index 0000000..1e44ccd --- /dev/null +++ b/src/pmdas/hotproc/help.fmt @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Run paragraphs of help text thru fmt(1) +# Lines with tabs or double spaces are run thru fmt(1) only for that line + +# source the PCP configuration environment variables +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +trap "rm -rf $tmp; exit" 0 1 2 3 15 + +$PCP_AWK_PROG -v tmpfile=$tmp/fmt ' + function fmt_line(l) { + print l > tmpfile + system("fmt -78 < " tmpfile) # format line(s) + close(tmpfile) + system(">" tmpfile) # truncate file + } + function format_line() { + if (line != "") { + fmt_line(line) + line = "" + } + } + BEGIN { line = "" } + /^#/ || /^@/ { format_line(); print; next } + / / || / / { format_line(); fmt_line($0); next } + /^$/ { format_line(); print; next } + { + if (line == "") + line = $0 + else + line = sprintf("%s\n%s",line,$0) + } + END { format_line() } +' diff --git a/src/pmdas/hotproc/help.hotproc b/src/pmdas/hotproc/help.hotproc new file mode 100644 index 0000000..30a74da --- /dev/null +++ b/src/pmdas/hotproc/help.hotproc @@ -0,0 +1,141 @@ +@ hotproc.cpuburn CPU utilization per "interesting" process +CPU utilization, or the fraction of time that each "interesting" process +was executing (user and system time) over the last refresh interval. +Also known as the "cpuburn" time. + +@ hotproc.control.refresh time in secs between refreshes +Controls how long it takes before the "interesting" process list is refreshed +and new cpuburn times (see hotproc.cpuburn) calculated. This value can be +changed at any time by using pmstore(1) if the permission is given during +installation of the hotproc PMDA. Once the value is changed, the instances +will not be available until after the new refresh period has elapsed. + +@ hotproc.control.config configuration predicate +The configuration predicate that is used to characterize "interesting" +processes. This will initially be the predicate as specified in the +configuration file. This value can be changed at any time by using +pmstore(1) if the permission is given during installation of the hotproc +PMDA. Once the value is changed, the instances will not be available +until after the refresh period has elapsed. + +@ hotproc.control.config_gen configuration generation number +Each time the configuration predicate is updated (see hotproc.control.config) +the configuration generation number is incremented. + +@ hotproc.total.cpuburn total amount of cpuburn over all "interesting" processes +The sum of the CPU utilization ("cpuburn" or the fraction of time that each +process was executing in user or system mode over the last refresh interval) +for all the "interesting" processes. + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuidle fraction of CPU idle time +The fraction of all CPU time classified as idle over the last refresh +interval. + +@ hotproc.total.cpuother.not_cpuburn total amount of cpuburn over all uninteresting processes +The sum of the CPU utilization ("cpuburn" or the fraction of time that +each process was executing in user or system mode over the last refresh +interval) for all the "uninteresting" processes. If this value is high in +comparison to hotproc.total.cpuburn, then configuration predicate of the +hotproc PMDA is classifying a significant fraction of the CPU utilization +to processes that are not "interesting". + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuother.transient fraction of time utilized by "transient" processes +The total CPU utilization (fraction of time that each process was executing +in user or system mode) for processes which are not present throughout +the most recent refreshes interval. The hotproc PMDA is limited to +selecting processes which are present throughout each refresh intervals. +If processes come and/or go during a refresh interval then they will never +be considered. This metric gives an indication of the level of activity of +these "transient" processes. If the value is large in comparison to the +sum of hotproc.total.cpuburn and hotproc.total.cpuother.not_cpuburn then +the "transient" processes are consuming lots of CPU time. Under these +circumstances, the hotproc PMDA may be less useful, or consideration +should be given to decreasing the value of the refresh interval +(hotproc.control.refresh) so fewer "transient" processes escape +consideration. + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuother.total total amount of cpuburn other than the "interesting" processes +Non-idle CPU utilization not accounted for by processes other than those +deemed "interesting". It is equivalent to hotproc.total.cpuother.not_cpuburn ++ hotproc.total.cpuother.transient. + +Values are in the range 0 to the number of CPUs. + +@ hotproc.total.cpuother.percent how much of the cpu for "transients" and uninterestings +Gives an indication of how much of the CPU time the "transient" processes +and the "uninteresting" processes are accounting for. Computed as: + 100 * hotproc.total.cpuother.total / number of CPUs + +@ hotproc.predicate.syscalls number of system calls per second over refresh interval +The number of system calls per second over the last refresh interval for +each "interesting" process. If the refresh interval spans times from t1 +to t2, then this is calculated by: + + m = hotproc.psusage.pu_sysc + + (m@t2 - m@t1) / (t2 - t1) + +@ hotproc.predicate.ctxswitch number of context switches per second over refresh interval +The number of context switches per second over the last refresh interval +for each "interesting" process. If the refresh interval spans times from +t1 to t2, then this is calculated by: + + m = hotproc.psusage.pu_vctx + hotproc.psusage.pu_ictx + + (m@t2 - m@t1) / (t2 - t1) + +@ hotproc.predicate.virtualsize virtual size of process in kilobytes at last refresh +The virtual size of each "interesting" process in kilobytes at the last +refresh time, calculated by: + + hotproc.psinfo.size + +@ hotproc.predicate.residentsize resident size of process in kilobytes at last refresh +The resident size of each "interesting" process in kilobytes at the last +refresh, calculated by: + + hotproc.psinfo.rssize + +@ hotproc.predicate.iodemand total kilobytes read and written per second over refresh interval +The total kilobytes read and written per second over the last refresh +interval for each "interesting" process. If the refresh interval spans +times from t1 to t2, then this is calculated by: + + // bytes read() + br = hotproc.psusage.bread@t2 - hotproc.psusage.bread@t1 + // Gigabytes read() + gbr = hotproc.psusage.gbread@t2 - hotproc.psusage.gbread@t1 + // bytes write() + bw = hotproc.psusage.bwrit@t2 - hotproc.psusage.bwrit@t1 + // Gigabytes write() + gbw = hotproc.psusage.gbwrit@t2 - hotproc.psusage.gbwrit@t1 + + ((gbr + gbw) * 1024 * 1024 + (br + bw) / 1024 ) / (t2 - t1) + +@ hotproc.predicate.iowait time in secs waiting for I/O per second over refresh interval +The fraction of time waiting for I/O for each "interesting" process over +refresh interval. If the refresh interval spans times from t1 to t2, +then this is calculated by: + + bw = (hotproc.accounting.timers.bwtime@t2 - + hotproc.accounting.timers.bwtime@t1) / 1000000000 + rw = (hotproc.accounting.timers.rwtime@t2 - + hotproc.accounting.timers.rwtime@t1) / 1000000000 + + (bw + rw) / (t2 - t1) + +@ hotproc.predicate.schedwait time in secs waiting on run queue per second over refresh interval +The fraction of time waiting on the run queue for each "interesting" +process over the last refresh interval. If the refresh interval spans +times from t1 to t2, then this is calculated by: + + qw = (hotproc.accounting.timers.qwtime@t2 - + hotproc.accounting.timers.qwtime@t1) / 1000000000 + + qw / (t2 - t1) diff --git a/src/pmdas/hotproc/pmns.hotproc b/src/pmdas/hotproc/pmns.hotproc new file mode 100644 index 0000000..0ea5a0e --- /dev/null +++ b/src/pmdas/hotproc/pmns.hotproc @@ -0,0 +1,34 @@ + +/* + * Metrics borrowed from "proc" pmda above ... these ones are specific + * to the hotproc PMDA. + */ + +hotproc.control { + refresh HOTPROC:100:1 + config HOTPROC:100:8 + config_gen HOTPROC:100:9 +} + +hotproc.total { + cpuidle HOTPROC:100:2 + cpuburn HOTPROC:100:3 + cpuother +} + +hotproc.total.cpuother { + transient HOTPROC:100:4 + not_cpuburn HOTPROC:100:5 + total HOTPROC:100:6 + percent HOTPROC:100:7 +} + +hotproc.predicate { + syscalls HOTPROC:101:0 + ctxswitch HOTPROC:101:1 + virtualsize HOTPROC:101:2 + residentsize HOTPROC:101:3 + iodemand HOTPROC:101:4 + iowait HOTPROC:101:5 + schedwait HOTPROC:101:6 +} diff --git a/src/pmdas/hotproc/root b/src/pmdas/hotproc/root new file mode 100644 index 0000000..0e14a7d --- /dev/null +++ b/src/pmdas/hotproc/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include <stdpmid> + +root { hotproc } + +#include "pmns" + diff --git a/src/pmdas/hotproc/sample.conf b/src/pmdas/hotproc/sample.conf new file mode 100644 index 0000000..6421e51 --- /dev/null +++ b/src/pmdas/hotproc/sample.conf @@ -0,0 +1,15 @@ +#pmdahotproc +Version 1.0 + +# Sample configuration file for hotproc PMDA +# +# It selects processes who are using more than 85% of the cpu +# over the refresh interval. + +cpuburn > 0.85 + +# Another example: +# Reporting processes which are either +# using greater than 85% cpu or +# are root processes using greater than 1% cpu +# ((cpuburn > 0.01) && (uname == "root")) || (cpuburn > 0.85) diff --git a/src/pmdas/hotproc/src/GNUmakefile b/src/pmdas/hotproc/src/GNUmakefile new file mode 100644 index 0000000..6000032 --- /dev/null +++ b/src/pmdas/hotproc/src/GNUmakefile @@ -0,0 +1,34 @@ +#!gmake +# +# Copyright (c) 2007 Silicon Graphics, Inc. All Rights Reserved. +# +# 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +TOPDIR = ../../../.. +include $(TOPDIR)/src/include/builddefs + +# This is just to get the source exported to the community. +# 'Makefile' in particular is included only for clues it might offer, +# and should almost certainly be removed from SRCFILES once +# the pmda is working. +LSRCFILES = Makefile config.c config.h ctltab.c error.c gram.y \ +gram_node.c gram_node.h hotproc.c hotproc.h lex.l pcpu.c pcpu.h \ +pglobal.c pglobal.h ppred_values.c ppred_values.h + +default default_pcp: +install install_pcp: +include $(BUILDRULES) + diff --git a/src/pmdas/hotproc/src/config.c b/src/pmdas/hotproc/src/config.c new file mode 100644 index 0000000..8710810 --- /dev/null +++ b/src/pmdas/hotproc/src/config.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/procfs.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "gram_node.h" +#include "gram.h" +#include "nameinfo.h" +#include "config.h" + +char *conf_buffer = NULL; /* contains config text */ +char *conf_buffer_ptr = NULL; +char *pred_buffer = NULL; /* contains parsed predicate */ + +static bool_node *the_tree = NULL; +static config_vars *the_vars = NULL; + +/* internal functions */ +static int eval_predicate(bool_node *); +static int eval_comparison(bool_node *); +static int eval_num_comp(N_tag, bool_node *, bool_node *); +static int eval_str_comp(N_tag, bool_node *, bool_node *); +static int eval_match_comp(N_tag, bool_node *, bool_node *); +static char* get_strvalue(bool_node *); +static double get_numvalue(bool_node *); +static void eval_error(char *); + +extern int parse_predicate(bool_node **); +extern char *pmProgname; +extern char *configfile; +extern FILE *yyin; + + + +FILE * +open_config(void) +{ + FILE *conf; + if ((conf = fopen(configfile, "r")) == NULL) { + (void)fprintf(stderr, "%s: Unable to open configuration file \"%s\": %s\n", + pmProgname, configfile, osstrerror()); + exit(1); + } + return conf; +} + +int +parse_config(bool_node **tree) +{ + int sts; + FILE *file = NULL; + char *fname; + struct stat stat_buf; + long size; + char *ptr; + + if ((sts = parse_predicate(tree)) != 0) { + (void)fprintf(stderr, "%s: Failed to parse configuration file\n", pmProgname); + return sts; + } + + /* --- dump to tmp file & read to buffer --- */ + if ((fname = tmpnam(NULL)) == NULL || + (file = fopen(fname, "w+")) == NULL) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to create \"%s\": %s\n", + pmProgname, fname, strerror(-sts)); + goto error; + } + if (unlink(fname) == -1) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to unlink \"%s\": %s\n", + pmProgname, fname, strerror(-sts)); + goto error; + } + dump_predicate(file, *tree); + fflush(file); + if (fstat(fileno(file), &stat_buf) < 0) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to stat \"%s\": %s\n", + pmProgname, fname, strerror(-sts)); + goto error; + } + size = (long)stat_buf.st_size; + ptr = malloc(size+1); + if (ptr == NULL) { + sts = -oserror(); + fprintf(stderr, "%s: parse_config: failed to malloc: %s\n", + pmProgname, strerror(-sts)); + goto error; + } + rewind(file); + if (fread(ptr, size, 1, file) != 1) { + clearerr(file); + fprintf(stderr, "%s: parse_config: failed to fread \"%s\"\n", + pmProgname, fname); + sts = -1; + goto error; + } + (void)fclose(file); + + if (pred_buffer != NULL) + free(pred_buffer); + pred_buffer = ptr; + pred_buffer[size] = '\0'; + return 0; + +error: + if (file != NULL) + (void)fclose(file); + return sts; +} + +void +new_tree(bool_node *tree) +{ + free_tree(the_tree); + the_tree = tree; +} + +void +read_config(FILE *conf) +{ + struct stat stat_buf; + long size; + int sts; + size_t nread; + + /* get length of file */ + sts = fstat(fileno(conf), &stat_buf); + if (sts < 0) { + (void)fprintf(stderr, "%s: Failure to stat configuration file \"%s\": %s\n", + pmProgname, configfile, osstrerror()); + exit(1); + } + size = (long)stat_buf.st_size; + + /* create buffer */ + conf_buffer = (char*)malloc(size+1*sizeof(char)); + if (conf_buffer == NULL) { + (void)fprintf(stderr, "%s: Failure to create buffer for configuration file \"%s\"\n", + pmProgname, configfile); + exit(1); + } + + conf_buffer_ptr = conf_buffer; + + /* read whole file into buffer */ + nread = fread(conf_buffer, sizeof(char), size, conf); + if (nread != size) { + (void)fprintf(stderr, "%s: Failure to read configuration file \"%s\" into buffer\n", + pmProgname, configfile); + exit(1); + } + conf_buffer[size] = '\0'; /* terminate the buffer */ + + if (parse_config(&the_tree) != 0) + exit(1); +} + +void +dump_tree(FILE *f) +{ + dump_bool_tree(f, the_tree); +} + + +int +eval_tree(config_vars *vars) +{ + the_vars = vars; + return eval_predicate(the_tree); +} + +/* + * do predicate testing for qa + */ + +#define QA_LINE 512 + +/* + * Return convention + * EOF = finished line or file + * 0 = error in input + * 1 = successful and continue + */ + +/* + * Read test vars of form: "var=value|var=value|var=value" + */ + +static int +read_test_var(char *line, config_vars *vars) +{ + const char EQUALS = '='; + const char DIVIDER = '|'; + char var[QA_LINE]; + char value[QA_LINE]; + static char *c; + int i = 0; + + /* if line is NULL then continue where left off */ + if (line != NULL) + c = line; + + if (*c == '\n') + return EOF; + + /* --- get variable name --- */ + i = 0; + while(*c != EQUALS && *c != '\n') { + var[i++] = *c++; + } + var[i] = '\0'; + + if (*c == '\n') { + fprintf(stderr, "%s: Error reading test variable, " + "looking for \"%c\"\n", pmProgname, EQUALS); + return 0; + } + + c++; /* skip over EQUALS */ + + /* --- get value --- */ + i = 0; + while(*c != DIVIDER && *c != '\n') { + value[i++] = *c++; + } + value[i] = '\0'; + + if (*c == DIVIDER) /* skip over DIVIDER */ + c++; + + /* --- var = value --- */ + if (strcmp(var, "uid") == 0) { + vars->uid = atoi(value); + } + else if (strcmp(var, "uname") == 0) { + if ((vars->uname = strdup(value)) == NULL) + goto failure; + } + else if (strcmp(var, "gid") == 0) { + vars->gid = atoi(value); + } + else if (strcmp(var, "gname") == 0) { + if ((vars->gname = strdup(value)) == NULL) + goto failure; + } + else if (strcmp(var, "fname") == 0) { + (void)strcpy(vars->fname, value); + } + else if (strcmp(var, "psargs") == 0) { + (void)strcpy(vars->psargs, value); + } + else if (strcmp(var, "cpuburn") == 0) { + vars->cpuburn = atof(value); + } + else if (strcmp(var, "syscalls") == 0) { + vars->preds.syscalls = atof(value); + } + else if (strcmp(var, "pu_sysc") == 0) { + vars->pu_sysc = atol(value); + } + else if (strcmp(var, "ctxswitch") == 0) { + vars->preds.ctxswitch = atof(value); + } + else if (strcmp(var, "pu_vctx") == 0) { + vars->pu_vctx = atol(value); + } + else if (strcmp(var, "pu_ictx") == 0) { + vars->pu_ictx = atol(value); + } + else if (strcmp(var, "virtualsize") == 0) { + vars->preds.virtualsize = atof(value); + } + else if (strcmp(var, "pr_size") == 0) { + vars->pr_size = atol(value); + } + else if (strcmp(var, "residentsize") == 0) { + vars->preds.residentsize = atof(value); + } + else if (strcmp(var, "pr_rssize") == 0) { + vars->pr_rssize = atol(value); + } + else if (strcmp(var, "iodemand") == 0) { + vars->preds.iodemand = atof(value); + } + else if (strcmp(var, "pu_gbread") == 0) { + vars->pu_gbread = atol(value); + } + else if (strcmp(var, "pu_bread") == 0) { + vars->pu_bread = atol(value); + } + else if (strcmp(var, "pu_gbwrit") == 0) { + vars->pu_gbwrit = atol(value); + } + else if (strcmp(var, "pu_bwrit") == 0) { + vars->pu_bwrit = atol(value); + } + else if (strcmp(var, "iowait") == 0) { + vars->preds.iowait = atof(value); + } + else if (strcmp(var, "ac_bwtime") == 0) { + vars->ac_bwtime = atoll(value); + } + else if (strcmp(var, "ac_rwtime") == 0) { + vars->ac_rwtime = atoll(value); + } + else if (strcmp(var, "schedwait") == 0) { + vars->preds.schedwait = atof(value); + } + else if (strcmp(var, "ac_qwtime") == 0) { + vars->ac_qwtime = atoll(value); + } + else { + fprintf(stderr, "%s: Error unrecognised test variable: \"%s\"\n", + pmProgname, var); + return 0; + } + + return 1; + +failure: + (void)fprintf(stderr, "%s: malloc failed for read_test_var()\n", pmProgname); + exit(1); +} + + +int +read_test_values(FILE *file, config_vars *vars) +{ + static char line[QA_LINE]; + int sts; + int i; + + if (fgets(line, QA_LINE-1, file) == NULL) + return EOF; + + if (strlen(line) == QA_LINE-1) { + fprintf(stderr, "%s: line limit exceeded\n", pmProgname); + return 0; + } + + /* note that line must end in '\n' */ + + /* reset all values */ + (void)memset(vars, 0, sizeof(*vars)); + + /* read each var=value pair for a line */ + for(i=0;/*forever*/;i++) { + sts = read_test_var((i==0?line:NULL), vars); + if (sts == EOF) + return 1; + if (sts == 0) + return 0; + } +} + + + +void +do_pred_testing(void) +{ + int sts; + config_vars vars; + FILE *conf = NULL; + + conf = open_config(); + read_config(conf); + (void)fclose(conf); + + dump_tree(stdout); + + for (;;) { + sts = read_test_values(stdin, &vars); + if (sts == EOF) + break; + if (sts == 0) { + (void)fprintf(stderr, "Bad input line\n"); + continue; + } + + if (eval_tree(&vars)) { + (void)fprintf(stdout, "true\n"); + } + else { + (void)fprintf(stdout, "false\n"); + } + } +} + + + +static void +eval_error(char *msg) +{ + (void)fprintf(stderr, "%s: Internal error : %s\n", pmProgname, msg?msg:""); + exit(1); +} + +static int +eval_predicate(bool_node *pred) +{ + bool_node *lhs, *rhs; + + switch(pred->tag) { + case N_and: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + return eval_predicate(lhs) && eval_predicate(rhs); + case N_or: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + return eval_predicate(lhs) || eval_predicate(rhs); + case N_not: + lhs = pred->data.children.left; + return !eval_predicate(lhs); + case N_true: + return 1; + case N_false: + return 0; + default: + return eval_comparison(pred); + }/*switch*/ +} + +static int +eval_comparison(bool_node *comp) +{ + bool_node *lhs = comp->data.children.left; + bool_node *rhs = comp->data.children.right; + + switch(comp->tag) { + case N_lt: case N_gt: case N_ge: case N_le: + case N_eq: case N_neq: + return eval_num_comp(comp->tag, lhs, rhs); + case N_seq: case N_sneq: + return eval_str_comp(comp->tag, lhs, rhs); + case N_match: case N_nmatch: + return eval_match_comp(comp->tag, lhs, rhs); + default: + eval_error("comparison"); + }/*switch*/ +} + +static int +eval_num_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + double x = get_numvalue(lhs); + double y = get_numvalue(rhs); + + switch(tag) { + case N_lt: return (x < y); + case N_gt: return (x > y); + case N_le: return (x <= y); + case N_ge: return (x >= y); + case N_eq: return (x == y); + case N_neq: return (x != y); + default: + eval_error("number comparison"); + }/*switch*/ +} + +static double +get_numvalue(bool_node *n) +{ + switch(n->tag) { + case N_number: return n->data.num_val; + case N_cpuburn: return the_vars->cpuburn; + case N_syscalls: return the_vars->preds.syscalls; + case N_ctxswitch: return the_vars->preds.ctxswitch; + case N_virtualsize: return the_vars->preds.virtualsize; + case N_residentsize: return the_vars->preds.residentsize; + case N_iodemand: return the_vars->preds.iodemand; + case N_iowait: return the_vars->preds.iowait; + case N_schedwait: return the_vars->preds.schedwait; + case N_gid: return the_vars->gid; + case N_uid: return the_vars->uid; + default: + eval_error("number value"); + } +} + +static int +eval_str_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + char *x = get_strvalue(lhs); + char *y = get_strvalue(rhs); + + switch(tag) { + case N_seq: return (strcmp(x,y)==0?1:0); + case N_sneq: return (strcmp(x,y)==0?0:1); + default: + eval_error("string comparison"); + }/*switch*/ +} + +static int +eval_match_comp(N_tag tag, bool_node *lhs, bool_node *rhs) +{ + int sts; + char *res; + char *str= get_strvalue(lhs); + char *pat = get_strvalue(rhs); + + if (rhs->tag != N_pat) { + eval_error("match"); + } + + res = re_comp(pat); + if (res != NULL) { + /* should have been checked at lex stage */ + /* => internal error */ + eval_error(res); + } + sts = re_exec(str); + if (sts < 0) { + eval_error("re_exec"); + } + + switch(tag) { + case N_match: return sts; + case N_nmatch: return !sts; + default: + eval_error("match comparison"); + }/*switch*/ +} + +static char * +get_strvalue(bool_node *n) +{ + + switch(n->tag) { + case N_str: + case N_pat: + return n->data.str_val; + case N_gname: + if (the_vars->gname != NULL) + return the_vars->gname; + else + return get_gname_info(the_vars->gid); + case N_uname: + if (the_vars->uname != NULL) + return the_vars->uname; + else + return get_uname_info(the_vars->uid); + case N_fname: return the_vars->fname; + case N_psargs: return the_vars->psargs; + default: + eval_error("string value"); + }/*switch*/ +} diff --git a/src/pmdas/hotproc/src/config.h b/src/pmdas/hotproc/src/config.h new file mode 100644 index 0000000..7de4186 --- /dev/null +++ b/src/pmdas/hotproc/src/config.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include <sys/procfs.h> + +typedef struct { + double syscalls; + double ctxswitch; + double virtualsize; + double residentsize; + double iodemand; + double iowait; + double schedwait; +} derived_pred_t; + + +typedef struct { + uid_t uid; /* real user id */ + gid_t gid; /* real group id */ + char *uname; + char *gname; + char fname[PRCOMSIZ]; /* basename of exec()'d pathname */ + char psargs[PRARGSZ]; /* initial chars of arg list */ + double cpuburn; + + derived_pred_t preds; + + /* --- ioctl buffer fields for testing purposes only --- */ + + /* prpsinfo_t fields */ + ulong_t pr_size; + ulong_t pr_rssize; + + /* prusage_t fields */ + ulong_t pu_sysc; + ulong_t pu_ictx; + ulong_t pu_vctx; + ulong_t pu_gbread; + ulong_t pu_bread; + ulong_t pu_gbwrit; + ulong_t pu_bwrit; + + /* accounting fields */ + accum_t ac_bwtime; + accum_t ac_rwtime; + accum_t ac_qwtime; + +} config_vars; + +#include "gram_node.h" + +FILE *open_config(void); +void read_config(FILE *); +int parse_config(bool_node **tree); +void new_tree(bool_node *tree); +int eval_tree(config_vars *); +void dump_tree(FILE *); +void do_pred_testing(void); +int read_test_values(FILE *, config_vars *); + +#endif diff --git a/src/pmdas/hotproc/src/ctltab.c b/src/pmdas/hotproc/src/ctltab.c new file mode 100644 index 0000000..534cbe0 --- /dev/null +++ b/src/pmdas/hotproc/src/ctltab.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <sys/procfs.h> +#include "pmapi.h" +#include "ctltab.h" +#include "pglobal.h" +#include "pmemory.h" +#include "pracinfo.h" +#include "pscred.h" +#include "psinfo.h" +#include "pstatus.h" +#include "psusage.h" +#include "pcpu.h" +#include "ppred_values.h" + +ctltab_entry ctltab[] = { + { CLUSTER_GLOBAL, 1, + pglobal_init, pglobal_getdesc, pglobal_setatom, pglobal_getinfo, pglobal_allocbuf }, + { 1, 1, + psinfo_init, psinfo_getdesc, psinfo_setatom, psinfo_getinfo, psinfo_allocbuf }, + { 2, 1, + pstatus_init, pstatus_getdesc, pstatus_setatom, pstatus_getinfo, pstatus_allocbuf }, + { 3, 1, + pscred_init, pscred_getdesc, pscred_setatom, pscred_getinfo, pscred_allocbuf }, + { 4, 1, + psusage_init, psusage_getdesc, psusage_setatom, psusage_getinfo, psusage_allocbuf }, + { 5, 1, + pmem_init, pmem_getdesc, pmem_setatom, pmem_getinfo, pmem_allocbuf }, + { 6, 1, + pracinfo_init, pracinfo_getdesc, pracinfo_setatom, pracinfo_getinfo, pracinfo_allocbuf }, + { CLUSTER_CPU, 1, + pcpu_init, pcpu_getdesc, pcpu_setatom, pcpu_getinfo, pcpu_allocbuf }, + { CLUSTER_PRED, 1, + ppred_init, ppred_getdesc, ppred_setatom, ppred_getinfo, ppred_allocbuf }, +}; + + +int nctltab = sizeof(ctltab) / sizeof (ctltab[0]); + +int +lookup_ctltab(int cluster) +{ + int i; + + for (i = 0; i < nctltab; i++) { + if (ctltab[i].cluster == cluster) + return i; + } + return PM_ERR_PMID; +} diff --git a/src/pmdas/hotproc/src/error.c b/src/pmdas/hotproc/src/error.c new file mode 100644 index 0000000..f71c5db --- /dev/null +++ b/src/pmdas/hotproc/src/error.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdlib.h> + +void +yywarn(s) +char *s; +{ + extern int yylineno; + + (void)fprintf(stderr, "Warning [line %d]\n%s\n", yylineno, s); +} + +void +yyerror(s) +char *s; +{ + extern int yylineno; + extern char yytext[]; + + (void)fprintf(stderr, "Specification error in configuration\n"); + (void)fprintf(stderr, "[line %d] %s: %s\n", yylineno, s, yytext); +} diff --git a/src/pmdas/hotproc/src/gram.y b/src/pmdas/hotproc/src/gram.y new file mode 100644 index 0000000..b98564f --- /dev/null +++ b/src/pmdas/hotproc/src/gram.y @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +%{ +#include <stdio.h> +#include "./gram_node.h" + +void yyerror(char *s); +int yylex(void); +int yyparse(void); + +int need_psusage = 0; +int need_accounting = 0; +static bool_node *pred_tree = NULL; + +%} + +%union { + char *y_str; + double y_number; + bool_node *y_node; + } + +%token <y_number> + NUMBER + +%token <y_str> + STRING + PATTERN + +%term AND OR NOT + LPAREN RPAREN TRUE FALSE + EQUAL NEQUAL + LTHAN LEQUAL GTHAN GEQUAL + MATCH NMATCH + GID UID CPUBURN GNAME UNAME FNAME PSARGS + SYSCALLS CTXSWITCH VIRTUALSIZE RESIDENTSIZE + IODEMAND IOWAIT SCHEDWAIT + VERSION + +%type <y_node> + predicate comparison + num_compar numvar + str_compar strvar + pattern_compar + +%type <y_number> + version + +%left OR +%left AND +%left NOT + +%% + +pred_tree: predicate { pred_tree = $1;} + | version predicate { pred_tree = $2;} + ; + +version: VERSION NUMBER { + float version_num = $2; + + if (version_num != 1.0) { + (void)fprintf(stderr, "Wrong version number in configuration predicate\n"); + (void)fprintf(stderr, "Expected version %.2f, but was given version %.2f .\n", + 1.0, version_num); + YYABORT; + } + } + +predicate: + predicate AND predicate { $$ = create_tnode(N_and, $1, $3); } + | predicate OR predicate { $$ = create_tnode(N_or, $1, $3); } + | NOT predicate { $$ = create_tnode(N_not, $2, NULL); } + | LPAREN predicate RPAREN { $$ = $2; } + | comparison + | TRUE { $$ = create_tag_node(N_true); } + | FALSE { $$ = create_tag_node(N_false); } + ; + +comparison: + num_compar + | str_compar + | pattern_compar + ; + +num_compar: + numvar LTHAN numvar { $$ = create_tnode(N_lt, $1, $3); } + | numvar LEQUAL numvar { $$ = create_tnode(N_le, $1, $3); } + | numvar GTHAN numvar { $$ = create_tnode(N_gt, $1, $3); } + | numvar GEQUAL numvar { $$ = create_tnode(N_ge, $1, $3); } + | numvar EQUAL numvar { $$ = create_tnode(N_eq, $1, $3); } + | numvar NEQUAL numvar { $$ = create_tnode(N_neq, $1, $3); } + ; + +numvar: NUMBER { $$ = create_number_node($1); } + | GID { $$ = create_tag_node(N_gid); } + | UID { $$ = create_tag_node(N_uid); } + | CPUBURN { $$ = create_tag_node(N_cpuburn); } + | SYSCALLS { need_psusage = 1; $$ = create_tag_node(N_syscalls); } + | CTXSWITCH { need_psusage = 1; $$ = create_tag_node(N_ctxswitch); } + | VIRTUALSIZE { $$ = create_tag_node(N_virtualsize); } + | RESIDENTSIZE { $$ = create_tag_node(N_residentsize); } + | IODEMAND { need_psusage = 1; $$ = create_tag_node(N_iodemand); } + | IOWAIT { need_accounting = 1; $$ = create_tag_node(N_iowait); } + | SCHEDWAIT { need_accounting = 1; $$ = create_tag_node(N_schedwait); } + ; + +str_compar: + strvar EQUAL strvar { $$ = create_tnode(N_seq, $1, $3); } + | strvar NEQUAL strvar { $$ = create_tnode(N_sneq, $1, $3); } + ; + +strvar: STRING { $$ = create_str_node($1); } + | GNAME { $$ = create_tag_node(N_gname); } + | UNAME { $$ = create_tag_node(N_uname); } + | FNAME { $$ = create_tag_node(N_fname); } + | PSARGS { $$ = create_tag_node(N_psargs); } + ; + +pattern_compar: + strvar MATCH PATTERN { $$ = create_tnode(N_match, $1, create_pat_node($3)); } + | strvar NMATCH PATTERN { $$ = create_tnode(N_nmatch, $1, create_pat_node($3)); } + ; + + +%% + +int +parse_predicate(bool_node **tree) +{ + int sts; + extern int yylineno; /* defined by lex */ + + yylineno=1; + + start_tree(); + sts = yyparse(); + + /* free any partial trees */ + if (sts != 0) { + free_tree(NULL); + return sts; + } + + *tree = pred_tree; + return 0; +} diff --git a/src/pmdas/hotproc/src/gram_node.c b/src/pmdas/hotproc/src/gram_node.c new file mode 100644 index 0000000..d2c36b5 --- /dev/null +++ b/src/pmdas/hotproc/src/gram_node.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "./gram_node.h" + +/* functions */ +static void dump_comparison(FILE *, bool_node *); +static void dump_var(FILE *, bool_node *); + +static bool_node *node_list = NULL; + +void start_tree(void) +{ + node_list = NULL; +} + +void free_tree(bool_node *root) +{ + bool_node *n, *next; + + if (root == NULL) + root = node_list; /* use last tree */ + + /* free all nodes in list */ + for (n = root; n != NULL; ) { + next = n->next; + if (n->tag == N_pat || n->tag == N_str) + free(n->data.str_val); + free(n); + n = next; + } + + if (root == node_list) + node_list = NULL; +} + +bool_node * +create_tag_node(N_tag tag) +{ + bool_node *new_node; + + new_node = (bool_node*)malloc(sizeof(bool_node)); + if (new_node == NULL) { + fprintf(stderr, "hotproc: malloc failed in config: %s", osstrerror()); + exit(1); + } + new_node->tag = tag; + + /* add to front of node-list */ + new_node->next = node_list; + node_list = new_node; + + return new_node; +} + +bool_node * +create_tnode(N_tag tag, bool_node *lnode, bool_node *rnode) +{ + bool_node *n = create_tag_node(tag); + n->data.children.left = lnode; + n->data.children.right = rnode; + return n; +} + +bool_node * +create_number_node(double x) +{ + bool_node *n = create_tag_node(N_number); + n->data.num_val = x; + return n; +} + + +bool_node *create_str_node(char *str) +{ + bool_node *n = create_tag_node(N_str); + n->data.str_val = str; + return n; +} + +bool_node *create_pat_node(char *str) +{ + bool_node *n = create_tag_node(N_pat); + n->data.str_val = str; + return n; +} + +void +dump_bool_tree(FILE *f, bool_node *tree) +{ + (void)fprintf(f, "--- bool tree ---\n"); + dump_predicate(f, tree); + (void)fprintf(f, "\n--- end bool tree ---\n"); +} + +void +dump_predicate(FILE *f, bool_node *pred) +{ + bool_node *lhs, *rhs; + + switch(pred->tag) { + case N_and: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + (void)fprintf(f, "("); + dump_predicate(f, lhs); + (void)fprintf(f, " && "); + dump_predicate(f, rhs); + (void)fprintf(f, ")"); + break; + case N_or: + lhs = pred->data.children.left; + rhs = pred->data.children.right; + (void)fprintf(f, "("); + dump_predicate(f, lhs); + (void)fprintf(f, " || "); + dump_predicate(f, rhs); + (void)fprintf(f, ")"); + break; + case N_not: + lhs = pred->data.children.left; + (void)fprintf(f, "(! "); + dump_predicate(f, lhs); + (void)fprintf(f, ")"); + break; + case N_true: + (void)fprintf(f, "(true)"); + break; + case N_false: + (void)fprintf(f, "(false)"); + break; + default: + dump_comparison(f, pred); + }/*switch*/ +} + +static void +dump_comparison(FILE *f, bool_node *comp) +{ + bool_node *lhs = comp->data.children.left; + bool_node *rhs = comp->data.children.right; + + (void)fprintf(f, "("); + dump_var(f, lhs); + switch(comp->tag) { + case N_lt: (void)fprintf(f, " < "); break; + case N_gt: (void)fprintf(f, " > "); break; + case N_le: (void)fprintf(f, " <= "); break; + case N_ge: (void)fprintf(f, " >= "); break; + case N_eq: (void)fprintf(f, " == "); break; + case N_seq: (void)fprintf(f, " == "); break; + case N_sneq: (void)fprintf(f, " != "); break; + case N_neq: (void)fprintf(f, " != "); break; + case N_match: (void)fprintf(f, " ~ "); break; + case N_nmatch: (void)fprintf(f, " !~ "); break; + default: (void)fprintf(f, "<ERROR>"); break; + }/*switch*/ + dump_var(f, rhs); + (void)fprintf(f, ")"); +} + +static void +dump_var(FILE *f, bool_node *var) +{ + switch(var->tag) { + case N_str: (void)fprintf(f, "\"%s\"", var->data.str_val); break; + case N_pat: (void)fprintf(f, "\"%s\"", var->data.str_val); break; + case N_number: (void)fprintf(f, "%f", var->data.num_val); break; + case N_uid: (void)fprintf(f, "uid"); break; + case N_gid: (void)fprintf(f, "gid"); break; + case N_uname: (void)fprintf(f, "uname"); break; + case N_gname: (void)fprintf(f, "gname"); break; + case N_fname: (void)fprintf(f, "fname"); break; + case N_psargs: (void)fprintf(f, "psargs"); break; + case N_cpuburn: (void)fprintf(f, "cpuburn"); break; + case N_syscalls: (void)fprintf(f, "syscalls"); break; + case N_ctxswitch: (void)fprintf(f, "ctxswitch"); break; + case N_virtualsize: (void)fprintf(f, "virtualsize"); break; + case N_residentsize: (void)fprintf(f, "residentsize"); break; + case N_iodemand: (void)fprintf(f, "iodemand"); break; + case N_iowait: (void)fprintf(f, "iowait"); break; + case N_schedwait: (void)fprintf(f, "schedwait"); break; + default: (void)fprintf(f, "<ERROR>"); break; + }/*switch*/ +} diff --git a/src/pmdas/hotproc/src/gram_node.h b/src/pmdas/hotproc/src/gram_node.h new file mode 100644 index 0000000..996208b --- /dev/null +++ b/src/pmdas/hotproc/src/gram_node.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GRAM_NODE_H +#define GRAM_NODE_H + +/* --- types --- */ +typedef enum +{ + N_and, N_or, N_not, + N_lt, N_le, N_gt, N_ge, + N_eq, N_neq, N_seq, N_sneq, + N_match, N_nmatch, + N_str, N_pat, N_number, + N_uid, N_gid, N_uname, N_gname, + N_fname, N_psargs, N_cpuburn, + N_true, N_false, + N_syscalls, N_ctxswitch, + N_virtualsize, N_residentsize, + N_iodemand, N_iowait, N_schedwait +} N_tag; + +typedef struct +{ + struct bool_node *left; + struct bool_node *right; +} bool_children; + +typedef struct bool_node +{ + N_tag tag; + struct bool_node *next; + union { + bool_children children; + char *str_val; + double num_val; + } + data; +} bool_node; + +/* --- functions --- */ + +void free_tree(bool_node *); +void start_tree(void); + +bool_node *create_tnode(N_tag, bool_node *, bool_node *); +bool_node *create_tag_node(N_tag); +bool_node *create_number_node(double); +bool_node *create_str_node(char *); +bool_node *create_pat_node(char *); +void dump_bool_tree(FILE *, bool_node *); +void dump_predicate(FILE *, bool_node *); + +#endif diff --git a/src/pmdas/hotproc/src/hotproc.c b/src/pmdas/hotproc/src/hotproc.c new file mode 100644 index 0000000..50abf0d --- /dev/null +++ b/src/pmdas/hotproc/src/hotproc.c @@ -0,0 +1,1555 @@ +/* + * Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <sys/stat.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/sysmp.h> +#include <sys/sysinfo.h> +#include <sys/procfs.h> +#include <sys/immu.h> +#include <sys/sysmacros.h> +#include <pwd.h> +#include <grp.h> + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +/* local stuff */ +#include "./cluster.h" +#include "../domain.h" +#include "./proc.h" +#include "./proc_aux.h" +#include "./psinfo.h" +#include "./psusage.h" +#include "./pglobal.h" +#include "./pcpu.h" +#include "./ctltab.h" +#include "./config.h" +#include "./hotproc.h" +#include "./pracinfo.h" +#include "./ppred_values.h" + +#define MIN_REFRESH 1 +#define INIT_PROC_MAX 200 + +#if (_MIPS_SZLONG == 32) +#define IO_PMTYPE PM_TYPE_U32 /* ulong_t from procfs.h/prusage */ +#define CTX_PMTYPE PM_TYPE_U32 /* ulong_t from procfs.h/prusage */ +#define SYSCALLS_PMTYPE PM_TYPE_U32 /* ulong_t from procfs.h/prusage */ +#define SYSIDLE_PMTYPE PM_TYPE_32 /* long=time_t from sysinfo.h/sysinfo */ +#define TIME_PMTYPE PM_TYPE_32 /* long from time.h or types.h */ +#define ACCUM_PMTYPE PM_TYPE_U64 /* accum_t from types.h */ +#endif /* _MIPS_SZLONG == 32 */ + +#if (_MIPS_SZLONG == 64) +#define IO_PMTYPE PM_TYPE_U64 /* ulong_t from procfs.h/prusage */ +#define CTX_PMTYPE PM_TYPE_U64 /* ulong_t from procfs.h/prusage */ +#define SYSCALLS_PMTYPE PM_TYPE_U64 /* ulong_t from procfs.h/prusage */ +#define SYSIDLE_PMTYPE PM_TYPE_64 /* long=time_t from sysinfo.h/sysinfo */ +#define TIME_PMTYPE PM_TYPE_64 /* long from time.h or types.h */ +#define ACCUM_PMTYPE PM_TYPE_U64 /* accum_t from types.h */ +#endif /* _MIPS_SZLONG == 64 */ + +extern char *conf_buffer; +extern char *conf_buffer_ptr; +extern char *pred_buffer; +char *configfile = NULL; +static int conf_gen = 1; /* configuration file generation number */ + +static int allow_stores = 1; /* allow stores or not */ + +static int hotproc_domain = 7; /* set in hotproc_init */ +static int pred_testing; /* just do predicate testing or not */ +static int parse_only; /* just parse config file and exit */ +static char* testing_fname; /* filename root for testing */ +static char *username; /* user account for pmda */ + +/* handle on /proc */ +static DIR *procdir; + +/* active list */ +static pid_t *active_list = NULL; /* generated per refresh */ +static int numactive = 0; +static int maxactive = INIT_PROC_MAX; + +/* process lists for current and previous */ +static process_t *proc_list[2] = {NULL, NULL}; + +/* array size allocated */ +static int maxprocs[2] = {INIT_PROC_MAX, INIT_PROC_MAX}; + +/* number of procs used in list (<= maxprocs) */ +static int numprocs[2] = {0, 0}; + +/* index into proc_list etc.. */ +static int current = 0; +static int previous = 1; + +/* refresh time interval in seconds - cmd arg */ +static struct timeval refresh_delta; + +/* event id for refreshing */ +static int refresh_afid; + +/* various cpu time totals */ +static int num_cpus = 0; +static int have_totals = 0; +static double transient; +static double cpuidle; +static double total_active; +static double total_inactive; + +/* number of refreshes */ +/* will wrap back to 2 */ +static unsigned long refresh_count = 0; + +/* format of an entry in /proc */ +char proc_fmt[8]; /* export for procfs fname conversions */ +int proc_entry_len; + +char *log = NULL; + +#ifndef USEC_PER_SEC +#define USEC_PER_SEC 1000000 /* number of usecs for 1 second */ +#endif +#ifndef NSEC_PER_SEC +#define NSEC_PER_SEC 1000000000 /* number of nsecs for 1 second */ +#endif + +#define ntime2double(x) \ + (((double)(x).tv_sec) + ((double)((ulong_t)((x).tv_nsec)))/NSEC_PER_SEC) + +#define utime2double(x) \ + (((double)(x).tv_sec) + ((double)((ulong_t)((x).tv_usec)))/USEC_PER_SEC) + +#define TWOe32 (double) 4.295e9 + + +/* + * Make initial allocation for proc list. + */ + +static int +init_proc_list(void) +{ + active_list = (pid_t*)malloc(INIT_PROC_MAX * sizeof(pid_t)); + proc_list[0] = (process_t*)malloc(INIT_PROC_MAX * sizeof(process_t)); + proc_list[1] = (process_t*)malloc(INIT_PROC_MAX * sizeof(process_t)); + if (proc_list[0] == NULL || proc_list[1] == NULL || active_list == NULL) + return -oserror(); + return 0; +} + +/* + * Work out the active list from the constraints. + */ + + +static void +init_active_list(void) +{ + numactive = 0; +} + +/* + * add_active_list: + * If unsuccessful in add - due to memory then return neg status. + * If member of active list return 1 + * If non-member of active list return 0 + */ + +static int +add_active_list(process_t *node, config_vars *vars) +{ + if (eval_tree(vars) == 0) { + return 0; + } + + if (numactive == maxactive) { + pid_t *res; + maxactive = numactive*2; + res = (pid_t *)realloc(active_list, maxactive * sizeof(pid_t)); + if (res == NULL) + return -osoerror(); + active_list = res; + } + active_list[numactive++] = node->pid; + return 1; +} + +#ifdef PCP_DEBUG + +static void +dump_active_list(void) +{ + int i = 0; + + (void)fprintf(stderr, "--- active list ---\n"); + for(i = 0; i < numactive; i++) { + (void)fprintf(stderr, "[%d] = %" FMT_PID "\n", i, active_list[i]); + }/*for*/ + (void)fprintf(stderr, "--- end active list ---\n"); +} + + +static void +dump_proc_list(void) +{ + int i; + process_t *node; + + (void)fprintf(stderr, "--- proc list ---\n"); + for (i = 0; i < numprocs[current]; i++) { + node = &proc_list[current][i]; + (void)fprintf(stderr, "[%d] = %" FMT_PID " ", i, node->pid); + (void)fprintf(stderr, "(syscalls = %ld) ", node->r_syscalls); + (void)fputc('\n', stderr); + }/*for*/ + (void)fprintf(stderr, "--- end proc list ---\n"); +} + +static void +dump_cputime(double pre_usr, double pre_sys, double post_usr, double post_sys) +{ + fprintf(stderr, "CPU Time: user = %f, sys = %f\n", + post_usr - pre_usr, post_sys - pre_sys); +} + +static void +dump_pred(derived_pred_t *pred) +{ + (void)fprintf(stderr, "--- pred vars ---\n"); + (void)fprintf(stderr, "syscalls = %f\n", pred->syscalls); + (void)fprintf(stderr, "ctxswitch = %f\n", pred->ctxswitch); + (void)fprintf(stderr, "virtualsize = %f\n", pred->virtualsize); + (void)fprintf(stderr, "residentsize = %f\n", pred->residentsize); + (void)fprintf(stderr, "iodemand = %f\n", pred->iodemand); + (void)fprintf(stderr, "iowait = %f\n", pred->iowait); + (void)fprintf(stderr, "schedwait = %f\n", pred->schedwait); + (void)fprintf(stderr, "--- end pred vars ---\n"); +} + +#endif + +/* + * Return 1 if pid is in active list. + * Return 0 if pid is NOT in active list. + */ + +static int +in_active_list(pid_t pid) +{ + int i; + + for(i = 0; i < numactive; i++) { + if (pid == active_list[i]) + return 1; + } + + return 0; +} + +static int +compar_pids(const void *n1, const void *n2) +{ + return ((process_t*)n2)->pid - ((process_t*)n1)->pid; +} + + +static void +set_proc_fmt(void) +{ + struct dirent *directp; /* go thru /proc directory */ + + for (rewinddir(procdir); directp=readdir(procdir);) { + if (!isdigit((int)directp->d_name[0])) + continue; + proc_entry_len = (int)strlen(directp->d_name); + (void)sprintf(proc_fmt, "%%0%dd", proc_entry_len); + break; + } +} + +/* + * look up node in proc list + */ +static process_t * +lookup_node(int curr_prev, pid_t pid) +{ + process_t key; + process_t *node; + + key.pid = pid; + + if ( (numprocs[curr_prev] > 0) && + ((node = bsearch(&key, proc_list[curr_prev], numprocs[curr_prev], + sizeof(process_t), compar_pids)) != NULL) ) { + return node; + } + return NULL; +} + +/* + * look up current node + */ +process_t * +lookup_curr_node(pid_t pid) +{ + return lookup_node(current, pid); +} + +/* + * Calculate difference allowing for single wrapping of counters + */ + +static double +DiffCounter(double current, double previous, int pmtype) +{ + double outval = current-previous; + + if (outval < 0.0) { + switch (pmtype) { + case PM_TYPE_32: + case PM_TYPE_U32: + outval += (double)UINT_MAX+1; + break; + case PM_TYPE_64: + case PM_TYPE_U64: + outval += (double)ULONGLONG_MAX+1; + break; + } + } + + return outval; +} + +static void +set_psinfo(prpsinfo_t *psinfo, config_vars *vars) +{ + psinfo->pr_size = (long)vars->pr_size; + psinfo->pr_rssize = (long)vars->pr_rssize; +} + +static void +set_psusage(prusage_t *psusage, config_vars *vars) +{ + psusage->pu_sysc = vars->pu_sysc; + psusage->pu_vctx = vars->pu_vctx; + psusage->pu_ictx = vars->pu_ictx; + psusage->pu_gbread = vars->pu_gbread; + psusage->pu_bread = vars->pu_bread; + psusage->pu_gbwrit = vars->pu_gbwrit; + psusage->pu_bwrit = vars->pu_bwrit; +} + +/* If have a test file then read in values + * and overwrite some variables in the proc buffer. + * Only read from file for each refresh => + * make each process have same line from file as values + * If read fails, then return -1 + * else if read something then return 0 + * else if not time to read then return 1 + */ + +static int +read_buf_file(int *rcount, char *suffix, FILE **testing_file, config_vars *vars) +{ + if (*testing_file == NULL) { + char fname[80]; + strcpy(fname, testing_fname); + strcat(fname, suffix); + *testing_file = fopen(fname, "r"); + if (*testing_file == NULL) { + fprintf(stderr, "%s: Unable to open test file \"%s\": %s\n", + pmProgname, fname, osstrerror()); + return -1; + } + } + /* only read new values for each refresh */ + if (refresh_count != *rcount) { + *rcount = (int)refresh_count; + if (read_test_values(*testing_file, vars) != 1) + return -1; + return 0; + } + + return 1; /* no values actually read */ +} + +static int +psusage_getbuf_file(pid_t pid, prusage_t *psusage) +{ + static config_vars vars; /* configuration variable values */ + static FILE *testing_file = NULL; + static int rcount = -1; /* last refresh_count */ + static int dont_read = 0; + int sts; + + if ((sts = psusage_getbuf(pid, psusage)) != 0) + return sts; + + if (testing_fname != NULL && !dont_read) { + sts = read_buf_file(&rcount, ".psusage", &testing_file, &vars); + if (sts >= 0) + set_psusage(psusage, &vars); + else + dont_read = 1; + } + return 0; +} + +static int +psinfo_getbuf_file(pid_t pid, prpsinfo_t *psinfo) +{ + static config_vars vars; /* configuration variable values */ + static FILE *testing_file = NULL; + static int rcount = -1; /* last refresh_count */ + static int dont_read = 0; + int sts; + + if ((sts = psinfo_getbuf(pid, psinfo)) != 0) + return sts; + + if (testing_fname != NULL && !dont_read) { + sts = read_buf_file(&rcount, ".psinfo", &testing_file, &vars); + if (sts >= 0) + set_psinfo(psinfo, &vars); + else + dont_read = 1; + } + return 0; +} + +static void +set_pracinfo(pracinfo_t *acct, config_vars *vars) +{ + acct->pr_timers.ac_bwtime = vars->ac_bwtime; + acct->pr_timers.ac_rwtime = vars->ac_rwtime; + acct->pr_timers.ac_qwtime = vars->ac_qwtime; +} + +static int +pracinfo_getbuf_file(pid_t pid, pracinfo_t *acct) +{ + static config_vars vars; /* configuration variable values */ + static FILE *testing_file = NULL; + static int rcount = -1; /* last refresh_count */ + static int dont_read = 0; + int sts; + + if ((sts = pracinfo_getbuf(pid, acct)) != 0) + return sts; + + if (testing_fname != NULL && !dont_read) { + sts = read_buf_file(&rcount, ".pracinfo", &testing_file, &vars); + if (sts >= 0) + set_pracinfo(acct, &vars); + else + dont_read = 1; + } + return 0; +} + +/* + * Refresh the process list for /proc entries. + */ + +static int +refresh_proc_list(void) +{ + extern int need_psusage; /* is psusage buffer needed or not */ + extern int need_accounting; /* is pracinfo buffer needed or not */ + + int sts; + int sysmp_sts; + pid_t pid; + struct dirent *directp; /* go thru /proc directory */ + prpsinfo_t psinfo; /* read in proc info */ + prusage_t psusage; /* read in proc usage */ + pracinfo_t acct; /* read in acct info */ + struct sysinfo sinfo; /* sysinfo from sysmp */ + process_t *oldnode = NULL; /* node from previous proc list */ + process_t *newnode = NULL; /* new node in current proc list */ + int np = 0; /* number of procs index */ + struct timeval p_timestamp; /* timestamp pre getting proc info */ + config_vars vars; /* configuration variable values */ + + struct timeval ts; + static double refresh_time[2]; /* timestamp after refresh */ + static time_t sysidle[2]; /* sys idle from sysmp */ + double sysidle_delta; /* system idle delta time since last refresh */ + double actual_delta; /* actual delta time since last refresh */ + double transient_delta; /* calculated delta time of transient procs */ + double cputime_delta; /* delta cpu time for a process */ + double syscalls_delta; /* delta num of syscalls for a process */ + double vctx_delta; /* delta num of valid ctx switches for a process */ + double ictx_delta; /* delta num of invalid ctx switches for a process */ + double bread_delta; /* delta num of bytes read */ + double gbread_delta; /* delta num of gigabytes read */ + double bwrit_delta; /* delta num of bytes written */ + double gbwrit_delta; /* delta num of gigabytes written */ + double bwtime_delta; /* delta num of nanosesc for waiting for blocked io */ + double rwtime_delta; /* delta num of nanosesc for waiting for raw io */ + double qwtime_delta; /* delta num of nanosesc waiting on run queue */ + double timestamp_delta; /* real time delta b/w refreshes for process */ + double total_cputime = 0; /* total of cputime_deltas for each process */ + double total_activetime = 0; /* total of cputime_deltas for active processes */ + double total_inactivetime = 0; /* total of cputime_deltas for inactive processes */ + +#ifdef PCP_DEBUG + double curr_time; /* for current time */ + double pre_usr, pre_sys; + double post_usr, post_sys; +#endif + + /* switch current and previous */ + if (current == 0) { + current = 1; previous = 0; + } + else { + current = 0; previous = 1; + } + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + __pmtimevalNow(&ts); + curr_time = utime2double(ts); + fprintf(stderr, "refresh_proc_list():\n"); + __pmProcessRunTimes(&pre_usr, &pre_sys); + } +#endif + +#ifdef PCP_DEBUG + /* used to verify externally (approx.) the values */ + /* note: doing this is _slow_ */ + if (pmDebug & DBG_TRACE_APPL2) { + static char cmd[80]; + sprintf(cmd, "(date ; ps -le )>> %s.refresh", log); + fprintf(stderr, "refresh: cmd = %s\n", cmd); + system(cmd); + } +#endif + + init_active_list(); + + (void)memset(&vars, 0, sizeof(config_vars)); + + for (np = 0, rewinddir(procdir); directp=readdir(procdir);) { + if (!isdigit((int)directp->d_name[0])) + continue; + (void)sscanf(directp->d_name, proc_fmt, &pid); + + __pmtimevalNow(&p_timestamp); + + if (psinfo_getbuf_file(pid, &psinfo) != 0) + continue; + + /* reallocate if run out of room */ + if (np == maxprocs[current]) { + process_t *res; + maxprocs[current] = np*2; + res = (process_t *)realloc(proc_list[current], + maxprocs[current] * sizeof(process_t)); + if (res == NULL) + return -oserror(); + proc_list[current] = res; + } + + newnode = &proc_list[current][np++]; + newnode->pid = pid; + newnode->r_cputime = ntime2double(psinfo.pr_time); + newnode->r_cputimestamp = utime2double(p_timestamp); + + if (need_psusage) { + if (psusage_getbuf_file(pid, &psusage) != 0) + continue; + newnode->r_syscalls = psusage.pu_sysc; + newnode->r_vctx = psusage.pu_vctx; + newnode->r_ictx = psusage.pu_ictx; + newnode->r_bread = psusage.pu_bread; + newnode->r_gbread = psusage.pu_gbread; + newnode->r_bwrit = psusage.pu_bwrit; + newnode->r_gbwrit = psusage.pu_gbwrit; + } + + if (need_accounting) { + if (pracinfo_getbuf_file(pid, &acct) != 0) + continue; + newnode->r_bwtime = acct.pr_timers.ac_bwtime; + newnode->r_rwtime = acct.pr_timers.ac_rwtime; + newnode->r_qwtime = acct.pr_timers.ac_qwtime; + } + + /* if we have a previous i.e. not the first time */ + if ((oldnode = lookup_node(previous, pid)) != NULL) { + + + cputime_delta = DiffCounter(newnode->r_cputime, oldnode->r_cputime, TIME_PMTYPE); + timestamp_delta = DiffCounter(newnode->r_cputimestamp, oldnode->r_cputimestamp, TIME_PMTYPE); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: " + "fname = \"%s\"; cputime = %f; elapsed = %f\n", + psinfo.pr_fname, cputime_delta, timestamp_delta); + } +#endif +#ifdef PCP_DEBUG + /* used to verify externally (approx.) the values */ + /* note: doing this is _slow_ */ + if (pmDebug & DBG_TRACE_APPL2) { + static char cmd[80]; + if (cputime_delta > timestamp_delta) { + sprintf(cmd, "(date ; ps -lp %" FMT_PID " )>> %s.refresh", pid, log); + fprintf(stderr, "refresh: cmd = %s\n", cmd); + system(cmd); + } + } +#endif + newnode->r_cpuburn = cputime_delta / timestamp_delta; + vars.cpuburn = newnode->r_cpuburn; + + if (need_psusage) { + + /* rate convert syscalls */ + syscalls_delta = DiffCounter((double)newnode->r_syscalls, + (double)oldnode->r_syscalls, SYSCALLS_PMTYPE); + vars.preds.syscalls = syscalls_delta / timestamp_delta; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: " + "syscalls = %f\n", vars.preds.syscalls); + } +#endif + + /* rate convert ctxswitch */ + vctx_delta = DiffCounter((double)newnode->r_vctx, + (double)oldnode->r_vctx, CTX_PMTYPE); + ictx_delta = DiffCounter((double)newnode->r_ictx, + (double)oldnode->r_ictx, CTX_PMTYPE); + vars.preds.ctxswitch = (vctx_delta + ictx_delta) / timestamp_delta; + + /* rate convert reading/writing (iodemand) */ + bread_delta = DiffCounter((double)newnode->r_bread, + (double)oldnode->r_bread, IO_PMTYPE); + gbread_delta = DiffCounter((double)newnode->r_gbread, + (double)oldnode->r_gbread, IO_PMTYPE); + bwrit_delta = DiffCounter((double)newnode->r_bwrit, + (double)oldnode->r_bwrit, IO_PMTYPE); + gbwrit_delta = DiffCounter((double)newnode->r_gbwrit, + (double)oldnode->r_gbwrit, IO_PMTYPE); + vars.preds.iodemand = (gbread_delta * 1024 * 1024 + + (double)bread_delta / 1024 + + gbwrit_delta * 1024 * 1024 + + (double)bwrit_delta / 1024) / + timestamp_delta; + + } + + if (need_accounting) { + + /* rate convert iowait */ + bwtime_delta = DiffCounter((double)newnode->r_bwtime, + (double)oldnode->r_bwtime, ACCUM_PMTYPE); + bwtime_delta /= 1000000000.0; /* nanosecs -> secs */ + rwtime_delta = DiffCounter((double)newnode->r_rwtime, + (double)oldnode->r_rwtime, ACCUM_PMTYPE); + rwtime_delta /= 1000000000.0; /* nanosecs -> secs */ + vars.preds.iowait = (bwtime_delta + rwtime_delta) / timestamp_delta; + + + /* rate convert schedwait */ + qwtime_delta = DiffCounter((double)newnode->r_qwtime, + (double)oldnode->r_qwtime, ACCUM_PMTYPE); + qwtime_delta /= 1000000000.0; /* nanosecs -> secs */ + vars.preds.schedwait = qwtime_delta / timestamp_delta; + + } + + newnode->preds = vars.preds; /* struct copy */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + dump_pred(&newnode->preds); + } +#endif + + } + else { + newnode->r_cpuburn = 0; + bzero(&newnode->preds, sizeof(newnode->preds)); + vars.cpuburn = 0; + vars.preds.syscalls = 0; + vars.preds.ctxswitch = 0; + vars.preds.iowait = 0; + vars.preds.schedwait = 0; + vars.preds.iodemand = 0; + cputime_delta = 0; + } + + total_cputime += cputime_delta; + + /* fix up vars record from psinfo */ + (void)strcpy(vars.fname, psinfo.pr_fname); + (void)strcpy(vars.psargs, psinfo.pr_psargs); + vars.uid = psinfo.pr_uid; + vars.gid = psinfo.pr_gid; + vars.uname = NULL; + vars.gname = NULL; + vars.preds.virtualsize = (double)psinfo.pr_size * (_pagesize / 1024); + vars.preds.residentsize = (double)psinfo.pr_rssize * (_pagesize / 1024); + + if ((sts = add_active_list(newnode, &vars)) < 0) { + return sts; + } + + if (sts == 0) + total_inactivetime += cputime_delta; + else + total_activetime += cputime_delta; + + }/*for each pid*/ + + numprocs[current] = np; + + __pmtimevalNow(&ts); + refresh_time[current] = utime2double(ts); + + refresh_count++; + /* If we wrap then say that we atleast have 2, + * inorder to indicate we have seen two successive refreshes. + */ + if (refresh_count == 0) + refresh_count = 2; + + bzero(&sinfo, sizeof(sinfo)); /* for purify */ + if ((sysmp_sts = (int)sysmp(MP_SAGET, MPSA_SINFO, &sinfo, sizeof(struct sysinfo))) < 0) { + __pmNotifyErr(LOG_ERR, "sysmp failed in refresh: %s\n", osstrerror()); + sysidle[current] = -1; + } + else { + sysidle[current] = sinfo.cpu[CPU_IDLE]; + } + + + have_totals = 0; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: refresh_count = %lu\n", + refresh_count); + } +#endif + + if (refresh_count > 1 && sysmp_sts != -1 && sysidle[previous] != -1) { + sysidle_delta = DiffCounter(sysidle[current], sysidle[previous], SYSIDLE_PMTYPE) / (double)HZ; + actual_delta = DiffCounter(refresh_time[current], refresh_time[previous], TIME_PMTYPE); + transient_delta = num_cpus * actual_delta - (total_cputime + sysidle_delta); + if (transient_delta < 0) /* sysidle is only accurate to 0.01 second */ + transient_delta = 0; + + have_totals = 1; + transient = transient_delta / actual_delta; + cpuidle = sysidle_delta / actual_delta; + total_active = total_activetime / actual_delta; + total_inactive = total_inactivetime / actual_delta; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + (void)fprintf(stderr, "refresh_proc_list: " + "total_cputime = %f\n", total_cputime); + (void)fprintf(stderr, "refresh_proc_list: " + "total_activetime = %f, total_inactivetime = %f\n", + total_activetime, total_inactivetime); + (void)fprintf(stderr, "refresh_proc_list: " + "sysidle_delta = %f, actual_delta = %f\n", + sysidle_delta, actual_delta); + (void)fprintf(stderr, "refresh_proc_list: " + "transient_delta = %f, transient = %f\n", + transient_delta, transient); + } +#endif + } + + + /* sort it for bsearching later */ + qsort(proc_list[current], numprocs[current], + sizeof(process_t), compar_pids); + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + __pmProcessRunTimes(&post_usr, &post_sys); + dump_proc_list(); + fprintf(stderr, "refresh_proc_list: duration = %f secs; ", + refresh_time[current] - curr_time); + dump_cputime(pre_usr, pre_sys, post_usr, post_sys); + dump_active_list(); + } +#endif + + return 0; +}/*refresh_proc_list*/ + + +static int +restart_proc_list(void) +{ + int sts; + + refresh_count = 0; + numprocs[current] = 0; /* clear the current list */ + sts = refresh_proc_list(); + + return sts; +} + +static void +timer_callback(int afid, void *data) +{ + int sts = refresh_proc_list(); + + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "timer_callback: refresh list failed: %s\n", pmErrStr(sts)); + } +} + + +static int +hotproc_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + __pmInResult *res; + int j; + int sts; + + + if (indom != proc_indom) + return PM_ERR_INDOM; + + if ((res = (__pmInResult *)malloc(sizeof(*res))) == NULL) + return -oserror(); + res->indom = indom; + res->instlist = NULL; + res->namelist = NULL; + sts = 0; + + if (name == NULL && inst == PM_IN_NULL) + res->numinst = numactive; + else + res->numinst = 1; + + if (inst == PM_IN_NULL) { + if ((res->instlist = (int *)malloc(res->numinst * sizeof(res->instlist[0]))) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + } + + if (name == NULL) { + if ((res->namelist = (char **)malloc(res->numinst * sizeof(res->namelist[0]))) == NULL) { + __pmFreeInResult(res); + return -oserror(); + } + for (j = 0; j < res->numinst; j++) + res->namelist[j] = NULL; + } + + /* --> names and ids (the whole instance map) */ + if (name == NULL && inst == PM_IN_NULL) { + /* find all instance ids and names for the PROC indom */ + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + (void)fprintf(stderr, "hotproc_instance: Getting whole instance map...\n"); + } +#endif + + /* go thru active list */ + for(j = 0; j < numactive; j++) { + res->instlist[j] = active_list[j]; + if ((sts = proc_id_to_mapname(active_list[j], &res->namelist[j])) < 0) + break; + } + } + /* id --> name */ + else if (name == NULL) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + (void)fprintf(stderr, "hotproc_instance: id --> name\n"); + } +#endif + if (!in_active_list(inst)) { + __pmNotifyErr(LOG_ERR, "proc_instance: pid not in active list %d\n", inst); + sts = PM_ERR_INST; + } + else { + sts = proc_id_to_name(inst, &res->namelist[0]); + } + } + /* name --> id */ + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + (void)fprintf(stderr, "hotproc_instance: name --> id\n"); + } +#endif + /* find the instance for the given indom/name */ + sts = proc_name_to_id(name, &res->instlist[0]); + if (!in_active_list(res->instlist[0])) { + __pmNotifyErr(LOG_ERR, "proc_instance: pid not in active list %d\n", + res->instlist[0]); + sts = PM_ERR_INST; + } + } + + if (sts == 0) + *result = res; + else + __pmFreeInResult(res); + + return sts; +} + +static int +hotproc_desc(pmID pmid, pmDesc *desc, pmdaExt *pmda) +{ + int pmidErr; + int ctl_i; + __pmID_int *pmidp = (__pmID_int *)&pmid; + + pmidErr = PM_ERR_PMID; + if (pmidp->domain == hotproc_domain) { + ctl_i = lookup_ctltab(pmidp->cluster); + if (ctl_i < 0) + pmidErr = ctl_i; + else + pmidErr = ctltab[ctl_i].getdesc(pmid, desc); + } + + return pmidErr; + +} + + +static int +hotproc_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i; /* over pmidlist[] */ + int ctl_i; /* over ctltab[] and fetched[] */ + int j; + int n; + int numvals; + int sts; + size_t need; + static pmResult *res = NULL; + static int maxnpmids = 0; + pmValueSet *vset; + pmDesc dp; + __pmID_int *pmidp; + pmAtomValue atom; + int aggregate_len; + static int max_numactive = 0; + static int **fetched = NULL; + + if (fetched == NULL) { + fetched = (int **)malloc(nctltab * sizeof(fetched[0])); + if (fetched == NULL) + return -oserror(); + memset(fetched, 0, nctltab * sizeof(fetched[0])); + } + + /* allocate for result structure */ + if (numpmid > maxnpmids) { + if (res != NULL) + free(res); + /* (numpmid - 1) because there's room for one valueSet in a pmResult */ + need = sizeof(pmResult) + (numpmid - 1) * sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == NULL) + return -oserror(); + maxnpmids = numpmid; + } + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + + /* fix up allocations for fetched array */ + for (ctl_i=1; ctl_i < nctltab; ctl_i++) { + if (numactive > max_numactive) { + int *f = (int*)realloc(fetched[ctl_i], numactive * sizeof(int)); + int ctl_j; /* over ctltab[] and fetched[] */ + if (f == NULL) { + max_numactive = 0; + for (ctl_j=1; ctl_j < nctltab; ctl_j++) { + if (fetched[ctl_j]) + free(fetched[ctl_j]); + fetched[ctl_j] = NULL; + } + return -oserror(); + } + fetched[ctl_i] = f; + } + (void)memset(fetched[ctl_i], 0, numactive * sizeof(int)); + if ((sts = ctltab[ctl_i].allocbuf(numactive)) < 0) + return sts; + }/*for*/ + if (numactive > max_numactive) { + max_numactive = numactive; + } + + sts = 0; + for (i = 0; i < numpmid; i++) { + int pmidErr = 0; + + pmidp = (__pmID_int *)&pmidlist[i]; + pmidErr = hotproc_desc(pmidlist[i], &dp, pmda); + + /* create a vset with the error code in it */ + if (pmidErr < 0) { + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)); + if (vset == NULL) { + if (i) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = pmidErr; + continue; + } + + if (pmidp->cluster == CLUSTER_GLOBAL) { + + /* global metrics, singular instance domain */ + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)); + if (vset == NULL) { + if (i > 0) { + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = 1; + + switch (pmidp->item) { + case ITEM_NPROCS: + { + char *path; + + /* pv#589180 + * Reduce the active list if processes have exitted. + */ + for (j=0; j < numactive; j++) { + pid_t pid = active_list[j]; + proc_pid_to_path(pid, NULL, &path, PINFO_PATH); + /* if process not found then remove from active list */ + if (access(path, R_OK) < 0) { + int ctl_k; + /* remove from active list */ + /* replace with end one */ + active_list[j] = active_list[numactive-1]; + /* also do same thing to fetched array */ + for(ctl_k = 1; ctl_k < nctltab; ctl_k++) { + fetched[ctl_k][j] = fetched[ctl_k][numactive-1]; + } + numactive--; + j--; /* test this slot again */ + } + } + + atom.l = numactive; + break; + } + case ITEM_REFRESH: + atom.l = (__int32_t)refresh_delta.tv_sec; + break; + case ITEM_CONFIG: + atom.cp = pred_buffer; + break; + case ITEM_CONFIG_GEN: + atom.l = conf_gen; + break; + case ITEM_TRANSIENT: + if (!have_totals) + vset->numval = 0; + else + atom.f = transient; + break; + case ITEM_CPUIDLE: + if (!have_totals) + vset->numval = 0; + else + atom.f = cpuidle; + break; + case ITEM_TOTAL_CPUBURN: + if (!have_totals) + vset->numval = 0; + else + atom.f = total_active; + break; + case ITEM_TOTAL_NOTCPUBURN: + if (!have_totals) + vset->numval = 0; + else + atom.f = total_inactive; + break; + case ITEM_OTHER_TOTAL: + if (!have_totals) + vset->numval = 0; + else + atom.f = transient + total_inactive; + break; + case ITEM_OTHER_PERCENT: + { + double other = transient + total_inactive; + double non_idle = other + total_active; + + /* if non_idle = 0, very unlikely, + * then the value here is meaningless + */ + if (!have_totals || non_idle == 0) + vset->numval = 0; + else + atom.f = other / non_idle * 100; + } + break; + } + + if (vset->numval == 1 ) { + sts = __pmStuffValue(&atom, 0, &vset->vlist[0], dp.type); + vset->valfmt = sts; + vset->vlist[0].inst = PM_IN_NULL; + } + } + else { + /* + * Multiple instance domain metrics. + */ + if ((ctl_i = lookup_ctltab(pmidp->cluster)) < 0) + return ctl_i; + if (!ctltab[ctl_i].supported) { + numvals = 0; + } + else if (pmidp->cluster == CLUSTER_PRED && !ppred_available(pmidp->item)) { + numvals = 0; + } + else { + pid_t pid; + + numvals = 0; + for (j = 0; j < numactive; j++) { + pid = active_list[j]; + if (!__pmInProfile(proc_indom, pmda->e_prof, pid)) + continue; + if (fetched[ctl_i][j] == 0) { + int sts; + if ((sts = ctltab[ctl_i].getinfo(pid, j)) == 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "hotproc_fetch: getinfo succeeded: " + "pid=%" FMT_PID ", j=%d\n", pid, j); + } +#endif + fetched[ctl_i][j] = 1; + numvals++; + } + else if (sts == -ENOENT) { + int ctl_k; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "hotproc_fetch: " + "getinfo failed: pid=%" FMT_PID ", j=%d\n", pid, j); + } +#endif + /* remove from active list */ + /* replace with end one */ + active_list[j] = active_list[numactive-1]; + /* also do same thing to fetched array */ + for(ctl_k = 1; ctl_k < nctltab; ctl_k++) { + fetched[ctl_k][j] = fetched[ctl_k][numactive-1]; + } + numactive--; + j--; /* test this slot again */ + } + } + else { + numvals++; + } + } + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "hotproc_fetch: numvals = %d\n", numvals); + } +#endif + + res->vset[i] = vset = (pmValueSet *)malloc( + sizeof(pmValueSet) + (numvals-1) * sizeof(pmValue)); + if (vset == NULL) { + if (i > 0) { /* not the first malloc failing */ + res->numpmid = i; + __pmFreeResultValues(res); + } + return -oserror(); + } + vset->pmid = pmidlist[i]; + vset->numval = numvals; + vset->valfmt = PM_VAL_INSITU; + + if (!ctltab[ctl_i].supported) { + vset->numval = PM_ERR_APPVERSION; + } + else { + if (numvals != 0) { + pid_t pid; + for (n=j=0; j < numactive; j++) { + pid = active_list[j]; + if (fetched[ctl_i][j] == 0) + continue; + + aggregate_len = ctltab[ctl_i].setatom(pmidp->item, &atom, j); + sts = __pmStuffValue(&atom, aggregate_len, &vset->vlist[n], dp.type); + vset->valfmt = sts; + vset->vlist[n].inst = pid; + n++; + }/*for*/ + }/*if*/ + }/*if*/ + + }/*if*/ + }/*for*/ + + *resp = res; + return 0; +}/*hotproc_fetch*/ + +static int +restart_refresh(void) +{ + int sts; + + /* + * Reschedule as if starting from init. + */ + sts = restart_proc_list(); + if (sts >= 0) { + /* Get rid of current event from queue, register new one */ + __pmAFunregister(refresh_afid); + refresh_afid = __pmAFregister(&refresh_delta, NULL, timer_callback); + } + + return sts; +} + +static int +hotproc_store(pmResult *result, pmdaExt *pmda) +{ + int i; + pmValueSet *vsp; + pmDesc desc; + __pmID_int *pmidp; + int sts = 0; + pmAtomValue av; + + if (!allow_stores) { + return PM_ERR_PERMISSION; + } + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + sts = hotproc_desc(vsp->pmid, &desc, pmda); + if (sts < 0) + break; + if (pmidp->cluster == CLUSTER_GLOBAL) { + switch (pmidp->item) { + case ITEM_REFRESH: + if (vsp->numval != 1 || vsp->valfmt != PM_VAL_INSITU) { + sts = PM_ERR_CONV; + } + break; + case ITEM_CONFIG: + if (vsp->numval != 1 || vsp->valfmt == PM_VAL_INSITU) { + sts = PM_ERR_CONV; + } + break; + default: + sts = PM_ERR_PERMISSION; + break; + } + } + else { + sts = PM_ERR_PERMISSION; + } + + if (sts < 0) + break; + + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + desc.type, &av, desc.type)) < 0) + break; + + switch (pmidp->item) { + case ITEM_REFRESH: + if (av.l < MIN_REFRESH) { + sts = PM_ERR_CONV; + } + else { + refresh_delta.tv_sec = av.l; + + if ((sts = restart_refresh()) < 0) + __pmNotifyErr(LOG_ERR, "hotproc_store: refresh list failed: %s\n", + pmErrStr(sts)); + } + break; + case ITEM_CONFIG: + { + bool_node *tree; + + conf_buffer_ptr = av.cp; + + /* Only accept the new config if its predicate is parsed ok */ + if (parse_config(&tree) != 0) { + conf_buffer_ptr = conf_buffer; + free(av.cp); + sts = PM_ERR_CONV; + } + else { + conf_gen++; + new_tree(tree); + free(conf_buffer); /* free old one */ + conf_buffer = av.cp; /* use the new one */ + if ((sts = restart_refresh()) < 0) + __pmNotifyErr(LOG_ERR, "hotproc_store: refresh list failed: %s\n", + pmErrStr(sts)); + } + } + break; + }/*switch*/ + + }/*for*/ + + return sts; +} + + + +/* + * Initialise the agent (only daemon and NOT DSO). + */ + +static void +hotproc_init(pmdaInterface *dp) +{ + int sts; + + __pmSetProcessIdentity(username); + + dp->version.two.fetch = hotproc_fetch; + dp->version.two.store = hotproc_store; + dp->version.two.desc = hotproc_desc; + dp->version.two.instance = hotproc_instance; + + /* + * Maintaining my own desc and indom table/info. + * This is why the passed in tables are NULL. + */ + pmdaInit(dp, NULL, 0, NULL, 0); + init_tables(dp->domain); + hotproc_domain = dp->domain; + + if ((procdir = opendir(PROCFS)) == NULL) { + dp->status = -oserror(); + __pmNotifyErr(LOG_ERR, "opendir(%s) failed: %s\n", PROCFS, osstrerror()); + return; + } + + set_proc_fmt(); + + + if ((num_cpus = (int)sysmp(MP_NPROCS)) < 0) { + dp->status = -oserror(); + __pmNotifyErr(LOG_ERR, "sysmp failed to get NPROCS: %s\n", osstrerror()); + return; + } + + if ((sts = init_proc_list()) < 0) { + dp->status = sts; + return; + } + + if ((sts = restart_proc_list()) < 0) { + dp->status = sts; + return; + } +} + + + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s [options] configfile\n\n", pmProgname); + (void)fputs("Options:\n" + " -C parse configfile and exit\n" + " -U username user account to run under (default \"pcp\")\n" + " -d domain use domain (numeric) for metrics domain of PMDA\n" + " -l logfile write log into logfile rather than using default log name\n" + " -s do NOT allow dynamic changes to the selection predicate\n" + " -t refresh set the refresh time interval in seconds\n", + stderr); + exit(1); +} + + +static void +get_options(int argc, char *argv[], pmdaInterface *dispatch) +{ + int n; + int err = 0; + char *err_msg; + + + while ((n = pmdaGetOpt(argc, argv, "CD:d:l:t:U:xX:?s", + dispatch, &err)) != EOF) { + switch (n) { + + case 'C': + parse_only = 1; + break; + + case 's': + allow_stores = 0; + break; + + case 't': + if (pmParseInterval(optarg, &refresh_delta, &err_msg) < 0) { + (void)fprintf(stderr, + "%s: -t requires a time interval: %s\n", + err_msg, pmProgname); + free(err_msg); + err++; + } + break; + + case 'U': + username = optarg; + break; + + case 'x': + /* hidden option for predicate testing */ + pred_testing = 1; + break; + + case 'X': + /* hidden option for predicate testing with iocntl buffers */ + testing_fname = optarg; + break; + + case '?': + err++; + } + } + + if (err || optind != argc -1) { + usage(); + } + + configfile = argv[optind]; +} + + + +/* + * Set up the agent for running as a daemon. + */ + +int +main(int argc, char **argv) +{ + pmdaInterface dispatch; + char *p; + int sep = __pmPathSeparator(); + int infd; + fd_set fds; + fd_set readyfds; + int nready; + int numfds; + FILE *conf; + char mypath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + refresh_delta.tv_sec = 10; + refresh_delta.tv_usec = 0; + + snprintf(mypath, sizeof(mypath), "%s%c" "hotproc" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, HOTPROC, + "hotproc.log", mypath); + + get_options(argc, argv, &dispatch); + + if (pred_testing) { + do_pred_testing(); + exit(0); + } + + if (!parse_only) { + pmdaOpenLog(&dispatch); + log = dispatch.version.two.ext->e_logfile; + } + + conf = open_config(); + read_config(conf); + (void)fclose(conf); + + if (parse_only) + exit(0); + + hotproc_init(&dispatch); + pmdaConnect(&dispatch); + + if ((infd = __pmdaInFd(&dispatch)) < 0) + exit(1); + FD_ZERO(&fds); + FD_SET(infd, &fds); + numfds = infd+1; + + refresh_afid = __pmAFregister(&refresh_delta, NULL, timer_callback); + + /* custom pmda main loop */ + for (;;) { + (void)memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(numfds, &readyfds, NULL, NULL, NULL); + + if (nready > 0) { + __pmAFblock(); + if (__pmdaMainPDU(&dispatch) < 0) + break; + __pmAFunblock(); + } + else if (nready < 0 && neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failed: %s\n", netstrerror()); + } + } + + exit(0); +} diff --git a/src/pmdas/hotproc/src/hotproc.h b/src/pmdas/hotproc/src/hotproc.h new file mode 100644 index 0000000..1c68fea --- /dev/null +++ b/src/pmdas/hotproc/src/hotproc.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PHOTPROC_H +#define PHOTPROC_H + +#include "config.h" + +/* main process node type */ +typedef struct process_t { + pid_t pid; + + /* refreshed data */ + ulong_t r_vctx; + ulong_t r_ictx; + ulong_t r_syscalls; + ulong_t r_bread; + ulong_t r_gbread; + ulong_t r_bwrit; + ulong_t r_gbwrit; + + float r_cpuburn; + double r_cputimestamp; + double r_cputime; + + accum_t r_bwtime; + accum_t r_rwtime; + accum_t r_qwtime; + + /* predicate values */ + derived_pred_t preds; + +} process_t; + +process_t * lookup_curr_node(pid_t); + +#endif diff --git a/src/pmdas/hotproc/src/lex.l b/src/pmdas/hotproc/src/lex.l new file mode 100644 index 0000000..d19f999 --- /dev/null +++ b/src/pmdas/hotproc/src/lex.l @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +%{ +#include "./gram_node.h" +#include "./gram.h" + +void yyerror(char *s); + +static char emsg[256]; /* error message */ +static char yy_ichar; /* input char from conf_buffer */ +extern char *conf_buffer_ptr; + +#undef input +#undef unput +#define input() ( (yy_ichar = *conf_buffer_ptr++) == '\n' ? (yylineno++, yy_ichar) : yy_ichar) +#define unput(c) { if ((*--conf_buffer_ptr = c) == '\n') yylineno--; } + +%} + +%option noinput +%option nounput + +%% + +Version { return VERSION; } +schedwait { return SCHEDWAIT; } +iowait { return IOWAIT; } +iodemand { return IODEMAND; } +residentsize { return RESIDENTSIZE; } +virtualsize { return VIRTUALSIZE; } +ctxswitch { return CTXSWITCH; } +syscalls { return SYSCALLS; } +gid { return GID; } +uid { return UID; } +uname { return UNAME; } +gname { return GNAME; } +fname { return FNAME; } +psargs { return PSARGS; } +cpuburn { return CPUBURN; } +"&&" { return AND; } +"||" { return OR; } +"!" { return NOT; } +"(" { return LPAREN; } +")" { return RPAREN; } +true { return TRUE; } +false { return FALSE; } +"==" { return EQUAL; } +"!=" { return NEQUAL; } +"<" { return LTHAN; } +"<=" { return LEQUAL; } +">" { return GTHAN; } +">=" { return GEQUAL; } +"~" { return MATCH; } +"!~" { return NMATCH; } + +\/[^/\n]*[/\n] { + char *str; + yylval.y_str = (char *)malloc(yyleng-1); + if (yylval.y_str == 0) { + (void)sprintf(emsg, "malloc failed: %s", osstrerror()); + yyerror(emsg); + } + strncpy(yylval.y_str, &yytext[1], yyleng-2); + yylval.y_str[yyleng-2] = '\0'; + if ((str = re_comp(yylval.y_str)) != 0) { + yyerror(str); + } + return PATTERN; + } + +\"[^"\n]*["\n] { + yylval.y_str = (char *)malloc(yyleng-1); + if (yylval.y_str == 0) { + (void)sprintf(emsg, "malloc failed: %s", osstrerror()); + yyerror(emsg); + } + strncpy(yylval.y_str, &yytext[1], yyleng-2); + yylval.y_str[yyleng-2] = '\0'; + return STRING; + } + + +[0-9]+ | +[0-9]*"."[0-9]+ | +[0-9]+"."[0-9]* { + yylval.y_number = atof(yytext); + return NUMBER; + } + +\#.*\n { } + +[\t \r\n]+ { } + + +[a-zA-Z]+ { + yyerror("Illegal word"); + } + +. { + yyerror("Illegal character"); + } +%% + diff --git a/src/pmdas/hotproc/src/pcpu.c b/src/pmdas/hotproc/src/pcpu.c new file mode 100644 index 0000000..da6abbd --- /dev/null +++ b/src/pmdas/hotproc/src/pcpu.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +#include <unistd.h> +#include <stdio.h> +#include <syslog.h> +#include <fcntl.h> +#include <ctype.h> +#include <pwd.h> + +#include "pmapi.h" +#include "impl.h" +#include "proc.h" +#include "proc_aux.h" +#include "cluster.h" +#include "pcpu.h" +#include "hotproc.h" + +static pmDesc desctab[] = { + /* hotproc.cpuburn */ + { PMID(CLUSTER_CPU,ITEM_CPUBURN), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + +}; + +static int ndesc = (sizeof(desctab)/sizeof(desctab[0])); + +static float *cpuburn_buf = NULL; + +void +pcpu_init(int dom) +{ + init_table(ndesc, desctab, dom); +} + +int +pcpu_getdesc(pmID pmid, pmDesc *desc) +{ + return getdesc(ndesc, desctab, pmid, desc); +} + +int +pcpu_setatom(int item, pmAtomValue *atom, int j) +{ + switch (item) { + case ITEM_CPUBURN: + atom->f = cpuburn_buf[j]; + break; + } + return 0; +} + +int +pcpu_getinfo(pid_t pid, int j) +{ + process_t *node; + char *path; + + node = lookup_curr_node(pid); + if (node == NULL) { + /* node should be there if it's in active list ! */ + (void)fprintf(stderr, "%s: Internal error for lookup_node()", pmProgname); + exit(1); + } + proc_pid_to_path(pid, NULL, &path, PINFO_PATH); + if (access(path, R_OK) < 0) + return -oserror(); + + cpuburn_buf[j] = node->r_cpuburn; + return 0; +} + + +int +pcpu_allocbuf(int size) +{ + static int max_size = 0; + float *cpub; + + if (size > max_size) { + cpub = realloc(cpuburn_buf, size * sizeof(float)); + if (cpub == NULL) + return -oserror(); + cpuburn_buf = cpub; + max_size = size; + } + + return 0; +} diff --git a/src/pmdas/hotproc/src/pcpu.h b/src/pmdas/hotproc/src/pcpu.h new file mode 100644 index 0000000..9bcf65c --- /dev/null +++ b/src/pmdas/hotproc/src/pcpu.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PCPU_H +#define PCPU_H + +#define CLUSTER_CPU 102 +#define ITEM_CPUBURN 0 + +void pcpu_init(int dom); +int pcpu_getdesc(pmID pmid, pmDesc *desc); +int pcpu_setatom(int item, pmAtomValue *atom, int j); +int pcpu_getinfo(pid_t pid, int j); +int pcpu_allocbuf(int size); + +#endif diff --git a/src/pmdas/hotproc/src/pglobal.c b/src/pmdas/hotproc/src/pglobal.c new file mode 100644 index 0000000..3c4a1ac --- /dev/null +++ b/src/pmdas/hotproc/src/pglobal.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +#include <unistd.h> +#include <stdio.h> +#include <syslog.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/stat.h> +#include <dirent.h> +#include <sys/procfs.h> +#include <sys/immu.h> +#include <sys/sysmacros.h> +#include <pwd.h> +#include <grp.h> + +#include "pmapi.h" + +#include "./proc.h" +#include "./proc_aux.h" +#include "./pglobal.h" + +static pmDesc desctab[] = { + { PMID(CLUSTER_GLOBAL,ITEM_NPROCS), + PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_REFRESH), + PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_CONFIG), + PM_TYPE_STRING, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_CONFIG_GEN), + PM_TYPE_32, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_CPUIDLE), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_TOTAL_CPUBURN), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_TRANSIENT), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_TOTAL_NOTCPUBURN), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_OTHER_TOTAL), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} }, + { PMID(CLUSTER_GLOBAL,ITEM_OTHER_PERCENT), + PM_TYPE_FLOAT, PM_INDOM_NULL, PM_SEM_INSTANT, {0,0,0,0,0,0} } +}; + +static int ndesc = (sizeof(desctab)/sizeof(desctab[0])); + + +void +pglobal_init(int dom) +{ + init_table(ndesc, desctab, dom); +} + +int +pglobal_getdesc(pmID pmid, pmDesc *desc) +{ + return getdesc(ndesc, desctab, pmid, desc); +} + +int +pglobal_setatom(int item, pmAtomValue *atom, int j) +{ + /* noop */ + return 0; +} + + +int +pglobal_getinfo(pid_t pid, int j) +{ + /* noop */ + return 0; +} + + +int +pglobal_allocbuf(int size) +{ + /* noop */ + return 0; +} diff --git a/src/pmdas/hotproc/src/pglobal.h b/src/pmdas/hotproc/src/pglobal.h new file mode 100644 index 0000000..659d7e0 --- /dev/null +++ b/src/pmdas/hotproc/src/pglobal.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PGLOBAL_H +#define PGLOBAL_H + +#define CLUSTER_GLOBAL 100 + +#define ITEM_NPROCS 0 +#define ITEM_REFRESH 1 +#define ITEM_CPUIDLE 2 +#define ITEM_TOTAL_CPUBURN 3 +#define ITEM_TRANSIENT 4 +#define ITEM_TOTAL_NOTCPUBURN 5 +#define ITEM_OTHER_TOTAL 6 +#define ITEM_OTHER_PERCENT 7 +#define ITEM_CONFIG 8 +#define ITEM_CONFIG_GEN 9 + +void pglobal_init(int dom); +int pglobal_getdesc(pmID pmid, pmDesc *desc); +int pglobal_setatom(int item, pmAtomValue *atom, int j); +int pglobal_getinfo(pid_t pid, int j); +int pglobal_allocbuf(int size); + +#endif diff --git a/src/pmdas/hotproc/src/ppred_values.c b/src/pmdas/hotproc/src/ppred_values.c new file mode 100644 index 0000000..98e17f1 --- /dev/null +++ b/src/pmdas/hotproc/src/ppred_values.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +#include <unistd.h> +#include <stdio.h> +#include <syslog.h> +#include <fcntl.h> +#include <ctype.h> +#include <pwd.h> + +#include "pmapi.h" +#include "impl.h" +#include "proc.h" +#include "proc_aux.h" +#include "cluster.h" +#include "ppred_values.h" +#include "hotproc.h" + + +static pmDesc desctab[] = { + /* hotproc.predicate.syscalls */ + { PMID(CLUSTER_PRED,ITEM_SYSCALLS), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,-1,1, 0,PM_TIME_SEC,0} }, + /* hotproc.predicate.ctxswitch */ + { PMID(CLUSTER_PRED,ITEM_CTXSWITCH), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,-1,1, 0,PM_TIME_SEC,0} }, + /* hotproc.predicate.virtualsize */ + { PMID(CLUSTER_PRED,ITEM_VIRTUALSIZE), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {1,0,0, PM_SPACE_KBYTE,0,0} }, + /* hotproc.predicate.residentsize */ + { PMID(CLUSTER_PRED,ITEM_RESIDENTSIZE), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {1,0,0, PM_SPACE_KBYTE,0,0} }, + /* hotproc.predicate.iodemand */ + { PMID(CLUSTER_PRED,ITEM_IODEMAND), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {1,-1,0, PM_SPACE_KBYTE,PM_TIME_SEC,0} }, + + /* + * NOTE + * iowait and schedwait really have units of sec/sec, i.e. utilization + */ + /* hotproc.predicate.iowait */ + { PMID(CLUSTER_PRED,ITEM_IOWAIT), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,0,0, 0,0,0} }, + /* hotproc.predicate.schedwait */ + { PMID(CLUSTER_PRED,ITEM_SCHEDWAIT), + PM_TYPE_FLOAT, PM_INDOM_PROC, PM_SEM_INSTANT, {0,0,0, 0,0,0} }, +}; + +static int ndesc = (sizeof(desctab)/sizeof(desctab[0])); + +static derived_pred_t *pred_buf = NULL; + +void +ppred_init(int dom) +{ + init_table(ndesc, desctab, dom); +} + +int +ppred_getdesc(pmID pmid, pmDesc *desc) +{ + return getdesc(ndesc, desctab, pmid, desc); +} + +int +ppred_available(int item) +{ + extern int need_psusage; /* is psusage buffer needed or not */ + extern int need_accounting; /* is pracinfo buffer needed or not */ + + switch (item) { + case ITEM_SYSCALLS: + case ITEM_CTXSWITCH: + case ITEM_IODEMAND: + return need_psusage; + case ITEM_IOWAIT: + case ITEM_SCHEDWAIT: + return need_accounting; + case ITEM_VIRTUALSIZE: + case ITEM_RESIDENTSIZE: + /* need_psinfo - always have it */ + return 1; + } + return 1; +} + +int +ppred_setatom(int item, pmAtomValue *atom, int j) +{ + switch (item) { + case ITEM_SYSCALLS: + atom->f = pred_buf[j].syscalls; + break; + case ITEM_CTXSWITCH: + atom->f = pred_buf[j].ctxswitch; + break; + case ITEM_VIRTUALSIZE: + atom->f = pred_buf[j].virtualsize; + break; + case ITEM_RESIDENTSIZE: + atom->f = pred_buf[j].residentsize; + break; + case ITEM_IODEMAND: + atom->f = pred_buf[j].iodemand; + break; + case ITEM_IOWAIT: + atom->f = pred_buf[j].iowait; + break; + case ITEM_SCHEDWAIT: + atom->f = pred_buf[j].schedwait; + break; + } + return 0; +} + +int +ppred_getinfo(pid_t pid, int j) +{ + process_t *node; + char *path; + + node = lookup_curr_node(pid); + if (node == NULL) { + /* node should be there if it's in active list ! */ + (void)fprintf(stderr, "%s: Internal error for lookup_node()", pmProgname); + exit(1); + } + proc_pid_to_path(pid, NULL, &path, PINFO_PATH); + if (access(path, R_OK) < 0) + return -oserror(); + + pred_buf[j] = node->preds; + return 0; +} + + +int +ppred_allocbuf(int size) +{ + static int max_size = 0; + derived_pred_t *predb; + + if (size > max_size) { + predb = realloc(pred_buf, size * sizeof(derived_pred_t)); + if (predb == NULL) + return -oserror(); + pred_buf = predb; + max_size = size; + } + + return 0; +} diff --git a/src/pmdas/hotproc/src/ppred_values.h b/src/pmdas/hotproc/src/ppred_values.h new file mode 100644 index 0000000..22a2332 --- /dev/null +++ b/src/pmdas/hotproc/src/ppred_values.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PPRED_H +#define PPRED_H + +#define CLUSTER_PRED 101 + +#define ITEM_SYSCALLS 0 +#define ITEM_CTXSWITCH 1 +#define ITEM_VIRTUALSIZE 2 +#define ITEM_RESIDENTSIZE 3 +#define ITEM_IODEMAND 4 +#define ITEM_IOWAIT 5 +#define ITEM_SCHEDWAIT 6 + +void ppred_init(int dom); +int ppred_getdesc(pmID pmid, pmDesc *desc); +int ppred_setatom(int item, pmAtomValue *atom, int j); +int ppred_getinfo(pid_t pid, int j); +int ppred_allocbuf(int size); +int ppred_available(int item); + +#endif |