diff options
Diffstat (limited to 'src/pmdas/summary')
-rw-r--r-- | src/pmdas/summary/GNUmakefile | 62 | ||||
-rw-r--r-- | src/pmdas/summary/Install | 51 | ||||
-rw-r--r-- | src/pmdas/summary/README | 255 | ||||
-rw-r--r-- | src/pmdas/summary/Remove | 38 | ||||
-rw-r--r-- | src/pmdas/summary/help | 56 | ||||
-rw-r--r-- | src/pmdas/summary/mainloop.c | 225 | ||||
-rw-r--r-- | src/pmdas/summary/pmda.c | 152 | ||||
-rw-r--r-- | src/pmdas/summary/pmns | 24 | ||||
-rw-r--r-- | src/pmdas/summary/root | 10 | ||||
-rw-r--r-- | src/pmdas/summary/summary.c | 300 | ||||
-rw-r--r-- | src/pmdas/summary/summary.h | 34 | ||||
-rw-r--r-- | src/pmdas/summary/summary.pmie | 40 |
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); |