summaryrefslogtreecommitdiff
path: root/src/pmdas/summary
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/summary')
-rw-r--r--src/pmdas/summary/GNUmakefile62
-rw-r--r--src/pmdas/summary/Install51
-rw-r--r--src/pmdas/summary/README255
-rw-r--r--src/pmdas/summary/Remove38
-rw-r--r--src/pmdas/summary/help56
-rw-r--r--src/pmdas/summary/mainloop.c225
-rw-r--r--src/pmdas/summary/pmda.c152
-rw-r--r--src/pmdas/summary/pmns24
-rw-r--r--src/pmdas/summary/root10
-rw-r--r--src/pmdas/summary/summary.c300
-rw-r--r--src/pmdas/summary/summary.h34
-rw-r--r--src/pmdas/summary/summary.pmie40
12 files changed, 1247 insertions, 0 deletions
diff --git a/src/pmdas/summary/GNUmakefile b/src/pmdas/summary/GNUmakefile
new file mode 100644
index 0000000..092e30d
--- /dev/null
+++ b/src/pmdas/summary/GNUmakefile
@@ -0,0 +1,62 @@
+#
+# Copyright (c) 1995-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.
+#
+
+TOPDIR = ../../..
+include $(TOPDIR)/src/include/builddefs
+
+IAM = summary
+DOMAIN = SYSSUMMARY
+TARGETS = $(IAM)$(EXECSUFFIX)
+
+HFILES = summary.h
+CFILES = summary.c pmda.c mainloop.c
+
+LSRCFILES = Install README Remove help pmns root summary.pmie
+LLDFLAGS= -L$(TOPDIR)/src/libpcp/src -L$(TOPDIR)/src/libpcp_pmda/src
+LLDLIBS = $(PCP_PMDALIB)
+LDIRT = domain.h *.log *.dir *.pag $(TARGETS)
+
+PMDADIR = $(PCP_PMDAS_DIR)/$(IAM)
+
+default: build-me
+
+include $(TOPDIR)/src/include/buildrules
+
+ifneq "$(TARGET_OS)" "mingw"
+build-me: $(TARGETS)
+
+install: build-me
+ $(INSTALL) -m 755 -d $(PMDADIR)
+ $(INSTALL) -m 755 $(IAM) $(PMDADIR)/pmda$(IAM)
+ $(INSTALL) -m 755 Install Remove $(PMDADIR)
+ $(INSTALL) -m 644 root README help pmns domain.h $(PMDADIR)
+ $(INSTALL) -m 644 summary.pmie $(PMDADIR)/expr.pmie
+else
+build-me:
+install:
+endif
+
+$(IAM)$(EXECSUFFIX): $(OBJECTS)
+
+mainloop.o summary.o pmda.o: summary.h
+summary.o pmda.o: domain.h
+
+domain.h: ../../pmns/stdpmid
+ $(DOMAIN_MAKERULE)
+
+default_%: default
+ @true
+
+install_%: install
+ @true
diff --git a/src/pmdas/summary/Install b/src/pmdas/summary/Install
new file mode 100644
index 0000000..590247a
--- /dev/null
+++ b/src/pmdas/summary/Install
@@ -0,0 +1,51 @@
+#! /bin/sh
+#
+# Copyright (c) 1997 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 summary PMDA and/or PMNS
+#
+
+. $PCP_DIR/etc/pcp.env
+. $PCP_SHARE_DIR/lib/pmdaproc.sh
+
+iam=summary
+pmda_interface=2
+forced_restart=false
+
+pmdaSetup
+
+if $do_pmda
+then
+ if [ ! -x $PCP_BIN_DIR/pmie ]
+ then
+ echo \
+'Error: The "summary" PMDA requires the pmie(1) application but this
+ does not appear to be installed.'
+ exit 1
+ fi
+
+ while true
+ do
+ $PCP_ECHO_PROG $PCP_ECHO_N "Interval between summary expression evaluation (seconds)? [10] ""$PCP_ECHO_C"
+ read delta
+ [ -z "$delta" ] && delta=10
+ [ -z "`echo $delta | tr -d '[0-9]'`" ] && break
+ echo "Error: interval \"$delta\" must be an integer, please try again"
+ done
+ args="$PCP_BIN_DIR/pmie -x -t $delta $PCP_PMDAS_DIR/summary/expr.pmie"
+ check_delay=15
+fi
+
+pmdaInstall
+
+exit 0
diff --git a/src/pmdas/summary/README b/src/pmdas/summary/README
new file mode 100644
index 0000000..c07fddd
--- /dev/null
+++ b/src/pmdas/summary/README
@@ -0,0 +1,255 @@
+Performance Co-Pilot PMDA for Exporting Metric Summaries
+========================================================
+
+This Performance Metrics Domain Agent (PMDA) is capable of collecting
+performance metrics values from other PMDAs, computing derived
+(summary) values, and exporting these derived values as performance
+metrics.
+
+This agent uses the Performance Metrics Inference Engine pmie(1) to
+periodically collect the data and compute the summary values. These
+derived values are typically computed by expressions that aggregate a
+number of base performance values, perhaps from a number of subsystems
+on the one host or even from multiple hosts, and perhaps over an
+extended period of time.
+
+All of the exported metrics have a singular instance and the values are
+"instantaneous", i.e. the exported value is the value as of the last
+time the summary was computed. Refer to the PMAPI(3) man page for more
+information about these terms.
+
+Metrics
+=======
+
+See the file ./help, or install the agent and execute the command
+
+ $ pminfo -fT summary
+
+Note that customization of the metrics made available by the summary
+PMDA is possible, as described below.
+
+Installation
+============
+
+ + # cd $PCP_PMDAS_DIR/summary
+
+ + Check that there is no clash in the Performance Metrics Domain
+ defined in ./domain.h and the other PMDAs currently in use (see
+ $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another
+ domain number.
+
+ + This PMDA caches the most recent value for the performance metrics
+ computed by pmie(1). The cached values are the ones returned via
+ the Performance Metrics Collection Demon pmcd(1) to clients. By
+ default pmie(1) evaluates the expressions once every 10 seconds.
+ The installation procedure will offer you the option to change this
+ interval.
+
+ + Then simply use
+
+ # ./Install
+
+ and choose both the "collector" and "monitor" installation
+ configuration options.
+
+ You will be prompted for the necessary information to set up
+ the summary agent.
+
+De-installation
+===============
+
+ + Simply use
+
+ # cd $PCP_PMDAS_DIR/summary
+ # ./Remove
+
+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/summary.log) should be checked for any warnings
+ or errors.
+
+Customization
+=============
+
+New summary metrics may be added as follows.
+
+ + Choose new Performance Metric Name Space (PMNS) names for the new
+ metrics. These must begin with "summary." and follow the rules
+ described in pmns(4).
+
+ For example summary.fs.wr_cache_hit and summary.fs.rd_cache_hit
+
+ + Edit the file ./pmns to add the new PMNS names in the format
+ described in pmns(4). You must choose a unique Performance Metric
+ Id (PMID) for each metric ... in the ./pmns file these will appear
+ as SYSSUMMARY:0:x for some x that is arbitrary in the range 0 to
+ 1023 and unique in this file.
+
+ For example
+
+ summary {
+ cpu
+ disk
+ netif
+ /*new*/ fs
+ }
+
+ ...
+
+ summary.fs {
+ wr_cache_hit SYSSUMMARY:0:10
+ rd_cache_hit SYSSUMMARY:0:11
+ }
+
+ + Use the local fake PMNS ./root and validate that the PMNS changes
+ are correct.
+
+ For example
+
+ $ pminfo -n root -m summary.fs
+ summary.fs.wr_cache_hit PMID: 27.0.10
+ summary.fs.rd_cache_hit PMID: 27.0.11
+
+ + Create a file (./expr.pmie in the examples below) containing the
+ new expressions. If the name to the left of the assignment
+ operator (=) is one of the PMNS names, then the pmie(1) expression
+ to the right will be evaluated and returned by the summary PMDA.
+ The expression must return a numeric value, which is exported as a
+ double precision floating point number.
+
+ If the expression has a set value, then only the first value is
+ exported (in most cases, the pmie aggregate operators should be used
+ to produce a scalar sum or average from a set of numeric values).
+
+ The exported metric has the dimension (space, time and count) of
+ the expression, and the scale is the canonical scale used by
+ pmie(1), namely bytes, seconds and counts.
+
+ For example
+
+ // filesystem buffer cache hit percentages
+ prefix = "kernel.all.io"; // variable, not exported
+ summary.fs.wr_cache_hit =
+ 100 - 100 * $prefix.bwrite / $prefix.lwrite;
+ summary.fs.rd_cache_hit =
+ 100 - 100 * $prefix.bread / $prefix.lread;
+
+ + Run pmie in debug mode to verify the expressions are being
+ evaluated correctly, and the values make sense.
+
+ For example
+
+ $ pmie -t2 -v expr.pmie
+ summary.fs.wr_cache_hit: ?
+ summary.fs.rd_cache_hit: ?
+
+ summary.fs.wr_cache_hit: 45.83
+ summary.fs.rd_cache_hit: 83.2
+
+ summary.fs.wr_cache_hit: 39.22
+ summary.fs.rd_cache_hit: 84.51
+
+ Once you are happy with the new expressions, add them to the existing
+ expressions in the file ./summary.pmie.
+
+ + Edit the ./help file to add help text for the new metrics ... see
+ newhelp(1) for a description of the syntax.
+
+ For example
+
+ @ summary.fs.wr_cache_hit Filesystem cache read hit ratio
+ Percentage of filesystem block writes that involve a block currently
+ found in the filesystem cache.
+
+ @ summary.fs.rd_cache_hit Filesystem cache write hit ratio
+ Percentage of filesystem block reads that involve a block currently
+ found in the filesystem cache, and thereby avoid a physical read.
+
+ + Install the new PMDA
+
+ For example
+
+ # ./Install
+
+ You will need to choose an appropriate configuration for installation of
+ the "summary" Performance Metrics Domain Agent (PMDA).
+
+ collector collect performance statistics on this system
+ monitor allow this system to monitor local and/or remote systems
+ both collector and monitor configuration for this system
+ Please enter c(ollector) or m(onitor) or b(oth) [b]
+
+ Updating the Performance Metrics Name Space ...
+ Installing pmchart view(s) ...
+
+ Interval between summary expression evaluation (seconds)? [10]
+ Terminate PMDA if already installed ...
+ Installing files ..
+ rm -f help.pag help.dir
+ $PCP_BINADM_DIR/newhelp help
+ Updating the PMCD control file, and notifying PMCD ...
+ Wait 15 seconds for the agent to initialize ...
+ Check summary metrics have appeared ... 8 metrics and 8 values
+
+ + Check the metrics ...
+
+ For example
+
+ $ pminfo -fT summary.fs
+
+ summary.fs.wr_cache_hit
+ Help:
+ Percentage of filesystem block writes that involve a block currently
+ found in the filesystem cache.
+ value 11.97916666666666
+
+ summary.fs.rd_cache_hit
+ Help:
+ Percentage of filesystem block reads that involve a block currently
+ found in the filesystem cache, and thereby avoid a physical read.
+ value 74.31192660550458
+
+ $ pmval -t5 -s4 summary.fs.wr_cache_hit
+
+ metric: summary.fs.wr_cache_hit
+ host: localhost
+ semantics: instantaneous value
+ units: none
+ samples: 8
+ interval: 5.00 sec
+
+ 63.60132158590308
+ 62.71878646441073
+ 62.71878646441073
+ 58.73968492123031
+ 58.73968492123031
+ 65.33822758259046
+ 65.33822758259046
+ 72.6099706744868
+
+ Note the values are being sampled here by pmval(1) every 5 seconds,
+ but pmie(1) is only passing new values to the sample PMDA every
+ 10 seconds. Both rates could be changed to suit the dynamics of
+ your new metrics.
+
+ + Create pmchart(1) views, pmview(1) scenes and pmlogger(1)
+ configurations to monitor and archive your new performance
+ metrics.
+
+ For example, a pmchart view could be created using pmchart and
+ the View->Save Configuration menu option. Copy the view from
+ $HOME/.pcp/pmchart into $PCP_PMDAS_DIR/summary and rename it to have
+ the suffix ".pmchart", and a prefix that identifies the view and
+ is unique amongst the view names in $PCP_VAR_DIR/config/pmchart,
+ e.g. Summary.FScache.pmchart
+
+ Then
+ # cp Summary.FScache.pmchart $PCP_VAR_DIR/config/pmchart/Summary.FScache
+
+ will install the view in a place where pmchart(1) will be able
+ to find it. Provided the name of the file ends in ".pmchart" in
+ $PCP_PMDAS_DIR/summary the view will be re-installed as a side-effect
+ of any subsequent ./Install of the summary PMDA.
diff --git a/src/pmdas/summary/Remove b/src/pmdas/summary/Remove
new file mode 100644
index 0000000..0ab6344
--- /dev/null
+++ b/src/pmdas/summary/Remove
@@ -0,0 +1,38 @@
+#! /bin/sh
+#
+# Copyright (c) 1997 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 summary PMDA
+#
+
+# Get standard environment
+. $PCP_DIR/etc/pcp.env
+
+# Get the common procedures and variable assignments
+#
+. $PCP_SHARE_DIR/lib/pmdaproc.sh
+
+# The name of the PMDA
+#
+iam=summary
+
+# Do it
+#
+pmdaSetup
+pmdaRemove
+
+exit 0
diff --git a/src/pmdas/summary/help b/src/pmdas/summary/help
new file mode 100644
index 0000000..5130a10
--- /dev/null
+++ b/src/pmdas/summary/help
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2000-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.
+#
+# 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
+#
+# summary PMDA help file in the ASCII format
+#
+# lines beginning with a # are ignored
+# lines beginning @ introduce a new entry of the form
+# @ metric_name oneline-text
+# help test goes
+# here over multiple lines
+# ...
+#
+# the metric_name is decoded against the default PMNS -- as a special case,
+# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an
+# instance domain identification, and the text describes the instance domain
+#
+# blank lines before the @ line are ignored
+#
+
+@ summary.cpu.util CPU utilization
+Fraction of time spent executing in user or system mode, averaged
+over all CPUs. Any time not accountered for here is spent either
+idle or waiting for I/O. Value in the range 0 to 1.
+
+@ summary.cpu.busy Proportion of the CPUs that are busy
+Fraction of the CPUs busy executing in user and/or system mode for more
+than 70% of the time. Value in the range 0 to 1.
+
+@ summary.disk.iops Average disk IOPS
+Average disk throughput in IO operations per second.
+
+@ summary.disk.busy Proportion of the disks that are busy
+Fraction of the disks busy serving at least 30 IOPs. Value in the
+range 0 to 1.
+
+@ summary.netif.packets Average network interface throughput
+Average network interface throughput in packets per second.
+
+@ summary.netif.busy Proportion of the network interfaces that are busy
+Fraction of network interfaces busy serving at least 375 packets per
+second). Value in the range 0 to 1.
+
diff --git a/src/pmdas/summary/mainloop.c b/src/pmdas/summary/mainloop.c
new file mode 100644
index 0000000..e59a467
--- /dev/null
+++ b/src/pmdas/summary/mainloop.c
@@ -0,0 +1,225 @@
+/*
+ * 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 "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "summary.h"
+
+static void (*freeResultCallback)(pmResult *) = __pmFreeResultValues;
+
+void
+mainLoopFreeResultCallback(void (*callback)(pmResult *res))
+{
+ freeResultCallback = callback;
+}
+
+void
+summaryMainLoop(char *pmdaname, int clientfd, pmdaInterface *dtp)
+{
+ __pmPDU *pb_pmcd;
+ __pmPDU *pb_client;
+ int sts;
+ pmID pmid;
+ pmDesc desc;
+ int npmids;
+ pmID *pmidlist;
+ pmResult *result;
+ int ctxnum;
+ __pmTimeval when;
+ int ident;
+ int type;
+ pmInDom indom;
+ int inst;
+ char *name;
+ __pmInResult *inres;
+ char *buffer;
+ __pmProfile *profile;
+ __pmProfile *saveprofile = NULL;
+ static fd_set readFds;
+ int maxfd;
+ int clientReady, pmcdReady;
+ int infd, outfd;
+
+ if (dtp->comm.pmda_interface != PMDA_INTERFACE_2) {
+ __pmNotifyErr(LOG_CRIT,
+ "summaryMainLoop supports PMDA protocol version 2 only, "
+ "not %d\n", dtp->comm.pmda_interface);
+ exit(1);
+ } else {
+ infd = dtp->version.two.ext->e_infd;
+ outfd = dtp->version.two.ext->e_outfd;
+ }
+
+ maxfd = infd+1;
+ if (clientfd >= maxfd)
+ maxfd = clientfd+1;
+
+ for ( ;; ) {
+ FD_ZERO(&readFds);
+ FD_SET(infd, &readFds);
+ FD_SET(clientfd, &readFds);
+
+ /* select here : block if nothing to do */
+ sts = select(maxfd, &readFds, NULL, NULL, NULL);
+
+ clientReady = FD_ISSET(clientfd, &readFds);
+ pmcdReady = FD_ISSET(infd, &readFds);
+
+ if (sts < 0)
+ break;
+ if (sts == 0)
+ continue;
+
+ if (clientReady) {
+ /*
+ * Service the command/client
+ */
+ sts = __pmGetPDU(clientfd, ANY_SIZE, TIMEOUT_NEVER, &pb_client);
+ if (sts < 0)
+ __pmNotifyErr(LOG_ERR, "client __pmGetPDU: %s\n", pmErrStr(sts));
+ if (sts <= 0)
+ /* End of File or error */
+ goto done;
+
+ service_client(pb_client);
+ __pmUnpinPDUBuf(pb_client);
+ }
+
+ if (pmcdReady) {
+ /* service pmcd */
+ sts = __pmGetPDU(infd, ANY_SIZE, TIMEOUT_NEVER, &pb_pmcd);
+
+ if (sts < 0)
+ __pmNotifyErr(LOG_ERR, "__pmGetPDU: %s\n", pmErrStr(sts));
+ if (sts <= 0)
+ /* End of File or error */
+ goto done;
+
+ switch (sts) {
+
+ case PDU_PROFILE:
+ /*
+ * can ignore ctxnum, since pmcd has already used this to send
+ * the correct profile, if required
+ */
+ if ((sts = __pmDecodeProfile(pb_pmcd, &ctxnum, &profile)) >= 0)
+ sts = dtp->version.two.profile(profile,
+ dtp->version.two.ext);
+ if (sts < 0)
+ __pmSendError(outfd, FROM_ANON, sts);
+ else {
+ if (saveprofile != NULL)
+ free(saveprofile);
+ /*
+ * need to keep the last valid one around, as the DSO
+ * routine just remembers the address
+ */
+ saveprofile = profile;
+ }
+ break;
+
+ case PDU_FETCH:
+ /*
+ * can ignore ctxnum, since pmcd has already used this to send
+ * the correct profile, if required
+ */
+ sts = __pmDecodeFetch(pb_pmcd, &ctxnum, &when, &npmids, &pmidlist);
+
+ /* Ignore "when"; pmcd should intercept archive log requests */
+ if (sts >= 0) {
+ sts = dtp->version.two.fetch(npmids, pmidlist, &result,
+ dtp->version.two.ext);
+ __pmUnpinPDUBuf(pmidlist);
+ }
+ if (sts < 0)
+ __pmSendError(outfd, FROM_ANON, sts);
+ else {
+ int st;
+ st =__pmSendResult(outfd, FROM_ANON, result);
+ if (st < 0) {
+ __pmNotifyErr(LOG_ERR,
+ "Cannot send fetch result: %s\n",
+ pmErrStr(st));
+ }
+ (*freeResultCallback)(result);
+ }
+ break;
+
+ case PDU_DESC_REQ:
+ if ((sts = __pmDecodeDescReq(pb_pmcd, &pmid)) >= 0) {
+ sts = dtp->version.two.desc(pmid, &desc,
+ dtp->version.two.ext);
+ }
+ if (sts < 0)
+ __pmSendError(outfd, FROM_ANON, sts);
+ else
+ __pmSendDesc(outfd, FROM_ANON, &desc);
+ break;
+
+ case PDU_INSTANCE_REQ:
+ if ((sts = __pmDecodeInstanceReq(pb_pmcd, &when, &indom, &inst, &name)) >= 0) {
+ /*
+ * Note: when is ignored.
+ * If we get this far, we are _only_ dealing
+ * with current data (pmcd handles the other
+ * cases).
+ */
+ sts = dtp->version.two.instance(indom, inst, name,
+ &inres,
+ dtp->version.two.ext);
+ }
+ if (sts < 0)
+ __pmSendError(outfd, FROM_ANON, sts);
+ else {
+ __pmSendInstance(outfd, FROM_ANON, inres);
+ __pmFreeInResult(inres);
+ }
+ break;
+
+ case PDU_TEXT_REQ:
+ if ((sts = __pmDecodeTextReq(pb_pmcd, &ident, &type)) >= 0) {
+ sts = dtp->version.two.text(ident, type, &buffer,
+ dtp->version.two.ext);
+ }
+ if (sts < 0)
+ __pmSendError(outfd, FROM_ANON, sts);
+ else
+ __pmSendText(outfd, FROM_ANON, ident, buffer);
+ break;
+
+ case PDU_RESULT:
+ if ((sts = __pmDecodeResult(pb_pmcd, &result)) >= 0)
+ sts = dtp->version.two.store(result,
+ dtp->version.two.ext);
+ __pmSendError(outfd, FROM_ANON, sts);
+ pmFreeResult(result);
+ break;
+
+ case PDU_ERROR:
+ /* end of context from PMCD ... we don't care */
+ break;
+
+ default:
+ fprintf(stderr, "%s: bogus pdu type: 0x%0x?\n", pmdaname, sts);
+ __pmSendError(outfd, FROM_ANON, PM_ERR_NYI);
+ break;
+ }
+ __pmUnpinPDUBuf(pb_pmcd);
+ }
+ }
+
+done:
+ return;
+}
diff --git a/src/pmdas/summary/pmda.c b/src/pmdas/summary/pmda.c
new file mode 100644
index 0000000..2a8828c
--- /dev/null
+++ b/src/pmdas/summary/pmda.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012 Red Hat.
+ * Copyright (c) 1995-2002 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 "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include <sys/stat.h>
+#include "summary.h"
+#include "domain.h"
+
+extern void summary_init(pmdaInterface *);
+extern void summary_done(void);
+
+pid_t clientPID;
+
+int
+main(int argc, char **argv)
+{
+ int errflag = 0;
+ int sep = __pmPathSeparator();
+ char **commandArgv;
+ pmdaInterface dispatch;
+ int i;
+ int len, c;
+ int clientPipe[2];
+ char helpfile[MAXPATHLEN];
+ int cmdpipe; /* metric source/cmd pipe */
+ char *command = NULL;
+ char *username;
+
+ __pmSetProgname(argv[0]);
+ __pmGetUsername(&username);
+ __pmSetInternalState(PM_STATE_PMCS); /* we are below the PMAPI */
+
+ snprintf(helpfile, sizeof(helpfile), "%s%c" "summary" "%c" "help",
+ pmGetConfig("PCP_PMDAS_DIR"), sep, sep);
+ pmdaDaemon (&dispatch, PMDA_INTERFACE_2, pmProgname, SYSSUMMARY,
+ "summary.log", helpfile);
+
+ while ((c = pmdaGetOpt(argc, argv, "H:h:D:d:l:U:",
+ &dispatch, &errflag)) != EOF) {
+ switch (c) {
+
+ case 'H': /* backwards compatibility, synonym for -h */
+ dispatch.version.two.ext->e_helptext = optarg;
+ break;
+
+ case 'U':
+ username = optarg;
+ break;
+
+ case '?':
+ errflag++;
+ break;
+ }
+ }
+
+ for (len=0, i=optind; i < argc; i++) {
+ len += strlen(argv[i]) + 1;
+ }
+ if (len == 0) {
+ fprintf(stderr, "%s: a command must be given after the options\n",
+ pmProgname);
+ errflag++;
+ }
+ else {
+ command = (char *)malloc(len+2);
+ command[0] = '\0';
+ for (i=optind; i < argc; i++) {
+ if (i > optind)
+ strcat(command, " ");
+ strcat(command, argv[i]);
+ }
+ }
+ commandArgv = argv + optind;
+
+ if (errflag) {
+ fprintf(stderr, "Usage: %s [options] command [arg ...]\n\n",
+ pmProgname);
+ fputs("Options:\n"
+ " -h helpfile help text file\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"
+ " -U username user account to run under (default \"pcp\")\n",
+ stderr);
+ exit(1);
+ }
+
+ /* force errors from here on into the log */
+ pmdaOpenLog(& dispatch);
+
+ /* switch to alternate user account now */
+ __pmSetProcessIdentity(username);
+
+ /* initialize */
+ summary_init(&dispatch);
+
+ pmdaConnect(&dispatch);
+ if (dispatch.status) {
+ fprintf (stderr, "Cannot connect to pmcd: %s\n",
+ pmErrStr(dispatch.status));
+ exit (1);
+ }
+
+ /*
+ * open a pipe to the command
+ */
+ if (pipe1(clientPipe) < 0) {
+ perror("pipe");
+ exit(oserror());
+ }
+
+ if ((clientPID = fork()) == 0) {
+ /* child */
+ char cmdpath[MAXPATHLEN+5];
+ close(clientPipe[0]);
+ if (dup2(clientPipe[1], fileno(stdout)) < 0) {
+ perror("dup");
+ exit(oserror());
+ }
+ close(clientPipe[1]);
+
+ snprintf (cmdpath, sizeof(cmdpath), "exec %s", commandArgv[0]);
+ execv(commandArgv[0], commandArgv);
+
+ perror(cmdpath);
+ exit(oserror());
+ }
+
+ fprintf(stderr, "clientPID = %" FMT_PID "\n", clientPID);
+
+ close(clientPipe[1]);
+ cmdpipe = clientPipe[0]; /* parent/agent reads from here */
+ __pmSetVersionIPC(cmdpipe, PDU_VERSION2);
+
+ summaryMainLoop(pmProgname, cmdpipe, &dispatch);
+
+ summary_done();
+ exit(0);
+}
diff --git a/src/pmdas/summary/pmns b/src/pmdas/summary/pmns
new file mode 100644
index 0000000..44f89fe
--- /dev/null
+++ b/src/pmdas/summary/pmns
@@ -0,0 +1,24 @@
+/*
+ * Metrics for summary PMDA
+ */
+
+summary {
+ cpu
+ disk
+ netif
+}
+
+summary.cpu {
+ util SYSSUMMARY:0:1
+ busy SYSSUMMARY:0:2
+}
+
+summary.disk {
+ iops SYSSUMMARY:0:3
+ busy SYSSUMMARY:0:4
+}
+
+summary.netif {
+ packets SYSSUMMARY:0:6
+ busy SYSSUMMARY:0:7
+}
diff --git a/src/pmdas/summary/root b/src/pmdas/summary/root
new file mode 100644
index 0000000..12754f3
--- /dev/null
+++ b/src/pmdas/summary/root
@@ -0,0 +1,10 @@
+/*
+ * fake "root" for validating the local PMNS subtree
+ */
+
+#include <stdpmid>
+
+root { summary }
+
+#include "pmns"
+
diff --git a/src/pmdas/summary/summary.c b/src/pmdas/summary/summary.c
new file mode 100644
index 0000000..a3da84a
--- /dev/null
+++ b/src/pmdas/summary/summary.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1995,2003 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 <ctype.h>
+#include <signal.h>
+#include "pmapi.h"
+#include "impl.h"
+#include "pmda.h"
+#include "summary.h"
+#include "domain.h"
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+int nmeta;
+meta_t *meta;
+pmResult *cachedResult;
+static int *freeList;
+
+static int
+summary_desc(pmID pmid, pmDesc *desc, pmdaExt * ex)
+{
+ static int i=0;
+
+ if ( i < nmeta && pmid == meta[i].desc.pmid) {
+found:
+ if (meta[i].desc.type == PM_TYPE_NOSUPPORT)
+ return PM_ERR_AGAIN; /* p76 rules ok */
+ *desc = meta[i].desc; /* struct assignment */
+ return 0; /* success */
+ }
+
+ for (i = 0; i < nmeta; i++) {
+ if (pmid == meta[i].desc.pmid)
+ goto found;
+ }
+ return PM_ERR_PMID;
+}
+
+void
+service_client(__pmPDU *pb)
+{
+ int n;
+ int i;
+ int j;
+ pmDesc desc;
+ pmDesc foundDesc;
+ pmResult *resp;
+ pmValueSet *vsp;
+ __pmPDUHdr *ph = (__pmPDUHdr *)pb;
+
+ switch (ph->type) {
+
+ case PDU_DESC:
+ if ((n = __pmDecodeDesc(pb, &desc)) < 0) {
+ fprintf(stderr, "service_client: __pmDecodeDesc failed: %s\n",
+ pmErrStr(n));
+ exit(1);
+ }
+
+ if (desc.indom != PM_INDOM_NULL) {
+ fprintf(stderr, "service_client: Warning: ignored desc for pmid=%s: indom is not singular\n", pmIDStr(desc.pmid));
+ return;
+ }
+
+ if (summary_desc(desc.pmid, &foundDesc, NULL) == 0) {
+ /* already in table */
+ fprintf(stderr,
+ "service_client: Warning: duplicate desc for pmid=%s\n",
+ pmIDStr(desc.pmid));
+ return;
+ }
+
+ nmeta++;
+ if ((meta = (meta_t *)realloc(meta, nmeta * sizeof(meta_t))) == NULL) {
+ __pmNoMem("service_client: meta realloc", nmeta * sizeof(meta_t), PM_FATAL_ERR);
+ }
+ memcpy(&meta[nmeta-1].desc, &desc, sizeof(pmDesc));
+
+ break;
+
+ case PDU_RESULT:
+ if ((n = __pmDecodeResult(pb, &resp)) < 0) {
+ fprintf(stderr, "service_client: __pmDecodeResult failed: %s\n", pmErrStr(n));
+ exit(1);
+ }
+
+ if (cachedResult == NULL) {
+ int need;
+ need = (int)sizeof(pmResult) - (int)sizeof(pmValueSet *);
+ if ((cachedResult = (pmResult *)malloc(need)) == NULL) {
+ __pmNoMem("service_client: result malloc", need, PM_FATAL_ERR);
+ }
+ cachedResult->numpmid = 0;
+ }
+
+ /*
+ * swap values from resp with those in cachedResult, expanding
+ * cachedResult if there are metrics we've not seen before
+ */
+ for (i = 0; i < resp->numpmid; i++) {
+ for (j = 0; j < cachedResult->numpmid; j++) {
+ if (resp->vset[i]->pmid == cachedResult->vset[j]->pmid) {
+ /* found matching PMID, update this value */
+ break;
+ }
+ }
+
+ if (j == cachedResult->numpmid) {
+ /* new PMID, expand cachedResult and initialize vset */
+ int need;
+ cachedResult->numpmid++;
+ need = (int)sizeof(pmResult) +
+ (cachedResult->numpmid-1) * (int)sizeof(pmValueSet *);
+ if ((cachedResult = (pmResult *)realloc(cachedResult, need)) == NULL) {
+ __pmNoMem("service_client: result realloc", need, PM_FATAL_ERR);
+ }
+ if ((cachedResult->vset[j] = (pmValueSet *)malloc(sizeof(pmValueSet))) == NULL) {
+ __pmNoMem("service_client: vset[]", sizeof(pmValueSet), PM_FATAL_ERR);
+ }
+ cachedResult->vset[j]->pmid = resp->vset[i]->pmid;
+ cachedResult->vset[j]->numval = 0;
+ }
+
+ /*
+ * swap vsets
+ */
+ vsp = cachedResult->vset[j];
+ cachedResult->vset[j] = resp->vset[i];
+ resp->vset[i] = vsp;
+
+ }
+
+ pmFreeResult(resp);
+ break;
+
+ case PDU_ERROR:
+ if ((n = __pmDecodeError(pb, &i)) < 0) {
+ fprintf(stderr, "service_client: __pmDecodeError failed: %s\n", pmErrStr(n));
+ exit(1);
+ }
+ fprintf(stderr, "service_client: Error PDU! %s\n", pmErrStr(i));
+ break;
+
+ default:
+ fprintf(stderr, "service_client: Bogus PDU type %d\n", ph->type);
+ exit(1);
+ }
+}
+
+static int
+summary_profile(__pmProfile *prof, pmdaExt * ex)
+{
+ /*
+ * doesn't make sense since summary metrics
+ * always have a singular instance domain.
+ */
+ return 0;
+}
+
+static int
+summary_instance(pmInDom indom, int inst, char *name, __pmInResult **result,
+ pmdaExt * ex)
+{
+ return PM_ERR_INDOM;
+}
+
+static void
+freeResultCallback(pmResult *res)
+{
+ int i;
+
+ /*
+ * pmResult has now been sent to pmcd. Only free the
+ * value sets that had no values available because
+ * the valid ones were reused from the cachedResult.
+ */
+ for (i=0; i < res->numpmid; i++) {
+ if (freeList[i])
+ free(res->vset[i]);
+ }
+ return;
+}
+
+
+static int
+summary_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt * ex)
+{
+ int i; /* over pmidlist[] */
+ int j; /* over vset->vlist[] */
+ int sts;
+ int need;
+ int validpmid;
+ pmID pmid;
+ pmDesc desc;
+ static pmResult *res = NULL;
+ static int maxnpmids = 0;
+
+ if (numpmid > maxnpmids) {
+ maxnpmids = numpmid;
+ 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();
+
+ if (freeList != NULL)
+ free(freeList);
+ if ((freeList = (int *)malloc(numpmid * sizeof(int))) == NULL)
+ return -oserror();
+ }
+ res->timestamp.tv_sec = 0;
+ res->timestamp.tv_usec = 0;
+ res->numpmid = numpmid;
+
+ for (i = 0; i < numpmid; i++) {
+ pmid = pmidlist[i];
+
+ /*
+ * do we know about the descriptor for this pmid?
+ * If not, then the error is PM_ERR_PMID
+ * regardless of whether there is an entry in
+ * the cached result.
+ */
+ sts = summary_desc(pmid, &desc, NULL);
+ validpmid = (sts == 0);
+
+ res->vset[i] = NULL;
+ freeList[i] = 1;
+
+ if (validpmid && cachedResult != NULL) {
+ for (j=0; j < cachedResult->numpmid; j++) {
+ if (pmid == cachedResult->vset[j]->pmid) {
+ res->vset[i] = cachedResult->vset[j];
+ freeList[i] = 0;
+ break;
+ }
+ }
+ }
+
+ if (!validpmid || res->vset[i] == NULL) {
+ /* no values available or the metric has no descriptor */
+ if ((res->vset[i] = (pmValueSet *)malloc(sizeof(pmValueSet))) == NULL)
+ return -oserror();
+ res->vset[i]->pmid = pmid;
+ res->vset[i]->valfmt = PM_VAL_INSITU;
+ res->vset[i]->numval = validpmid ? PM_ERR_VALUE : sts;
+ }
+ }
+ *resp = res;
+
+ return numpmid;
+}
+
+static int
+summary_store(pmResult *result, pmdaExt * ex)
+{
+ return PM_ERR_PERMISSION;
+}
+
+void
+summary_init(pmdaInterface *dp)
+{
+ void (*callback)() = freeResultCallback;
+
+ dp->version.two.profile = summary_profile;
+ dp->version.two.fetch = summary_fetch;
+ dp->version.two.desc = summary_desc;
+ dp->version.two.instance = summary_instance;
+ dp->version.two.store = summary_store;
+
+ mainLoopFreeResultCallback(callback);
+
+ pmdaInit(dp, NULL, 0, NULL, 0);
+}
+
+void
+summary_done(void)
+{
+ int st;
+
+ fprintf(stderr, "summary agent pid=%" FMT_PID " done\n", getpid());
+ kill(clientPID, SIGINT);
+ waitpid(clientPID, &st, 0);
+}
diff --git a/src/pmdas/summary/summary.h b/src/pmdas/summary/summary.h
new file mode 100644
index 0000000..ccc0613
--- /dev/null
+++ b/src/pmdas/summary/summary.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/*
+ * exported summary metrics
+ */
+typedef struct {
+ char *name; /* name space path */
+ pmDesc desc; /* descriptor, inc. pmid */
+} meta_t;
+
+extern meta_t *meta;
+extern int nmeta;
+extern char *command;
+extern char *helpfile;
+extern int cmdpipe;
+extern pid_t clientPID;
+
+extern void summaryMainLoop(char *, int, pmdaInterface *);
+extern void mainLoopFreeResultCallback(void (*)(pmResult *));
+extern void service_client(__pmPDU *);
+
+
diff --git a/src/pmdas/summary/summary.pmie b/src/pmdas/summary/summary.pmie
new file mode 100644
index 0000000..bedb824
--- /dev/null
+++ b/src/pmdas/summary/summary.pmie
@@ -0,0 +1,40 @@
+//
+// summary metrics
+//
+// these expressions are evaluated by pmie(1)
+//
+// use the default interval between expression evaluation (currently
+// 10 seconds), and re-configure via -t command line arg to pmie
+// (see Install)
+
+// CPU utilization
+//
+cpuse = "(kernel.percpu.cpu.sys + kernel.percpu.cpu.user)";
+ncpu = "hinv.ncpu";
+
+// average CPU utilization
+summary.cpu.util = avg_inst $cpuse;
+
+// proportion of CPUs that are busy
+summary.cpu.busy = (count_inst $cpuse > 0.7) / $ncpu;
+
+// Disk utilization
+//
+diskio = "disk.dev.total";
+ndisk = "hinv.ndisk";
+
+// average spindle activity
+summary.disk.iops = avg_inst ($diskio);
+
+// proportion of disk spindles that are busy
+summary.disk.busy = (count_inst $diskio > 40) / $ndisk;
+
+// Network interface utilization
+//
+netio = "network.interface.total.packets";
+
+// average network interface activity
+summary.netif.packets = avg_inst ($netio);
+
+// proportion of network interfaces that are busy
+summary.netif.busy = (count_inst $netio > 400) / (count_inst $netio >= 0);