summaryrefslogtreecommitdiff
path: root/src/pmdas/hotproc
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/hotproc
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/hotproc')
-rw-r--r--src/pmdas/hotproc/GNUakefile52
-rw-r--r--src/pmdas/hotproc/GNUmakefile157
-rw-r--r--src/pmdas/hotproc/Install150
-rw-r--r--src/pmdas/hotproc/README141
-rw-r--r--src/pmdas/hotproc/Remove38
-rw-r--r--src/pmdas/hotproc/fixpmns.awk35
-rw-r--r--src/pmdas/hotproc/general.conf27
-rwxr-xr-xsrc/pmdas/hotproc/general.pmie29
-rwxr-xr-xsrc/pmdas/hotproc/help.fmt36
-rw-r--r--src/pmdas/hotproc/help.hotproc141
-rw-r--r--src/pmdas/hotproc/pmns.hotproc34
-rw-r--r--src/pmdas/hotproc/root10
-rw-r--r--src/pmdas/hotproc/sample.conf15
-rw-r--r--src/pmdas/hotproc/src/GNUmakefile34
-rw-r--r--src/pmdas/hotproc/src/config.c569
-rw-r--r--src/pmdas/hotproc/src/config.h79
-rw-r--r--src/pmdas/hotproc/src/ctltab.c66
-rw-r--r--src/pmdas/hotproc/src/error.c40
-rw-r--r--src/pmdas/hotproc/src/gram.y163
-rw-r--r--src/pmdas/hotproc/src/gram_node.c199
-rw-r--r--src/pmdas/hotproc/src/gram_node.h69
-rw-r--r--src/pmdas/hotproc/src/hotproc.c1555
-rw-r--r--src/pmdas/hotproc/src/hotproc.h52
-rw-r--r--src/pmdas/hotproc/src/lex.l115
-rw-r--r--src/pmdas/hotproc/src/pcpu.c100
-rw-r--r--src/pmdas/hotproc/src/pcpu.h31
-rw-r--r--src/pmdas/hotproc/src/pglobal.c93
-rw-r--r--src/pmdas/hotproc/src/pglobal.h41
-rw-r--r--src/pmdas/hotproc/src/ppred_values.c163
-rw-r--r--src/pmdas/hotproc/src/ppred_values.h39
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