diff options
Diffstat (limited to 'src/pmie')
97 files changed, 14955 insertions, 0 deletions
diff --git a/src/pmie/GNUmakefile b/src/pmie/GNUmakefile new file mode 100644 index 0000000..48c46ec --- /dev/null +++ b/src/pmie/GNUmakefile @@ -0,0 +1,68 @@ +# +# Copyright (c) 2013-2014 Red Hat. +# 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. +# + +TOPDIR = ../.. +include $(TOPDIR)/src/include/builddefs + +SUBDIRS = src examples +OTHERS = control stomp rc_pmie pmie2col.sh pmie_check.sh pmie_daily.sh + +LSRCFILES = $(OTHERS) crontab.in +LDIRT = crontab pmie.service + +ifeq ($(TARGET_OS),linux) +CRONTAB_USER = $(PCP_USER) +CRONTAB_PATH = $(PCP_ETC_DIR)/cron.d/pcp-pmie +else +CRONTAB_USER = +CRONTAB_PATH = $(PCP_SYSCONF_DIR)/pmie/crontab +endif + +default:: crontab pmie.service + +default:: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install:: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install:: default + $(INSTALL) -m 775 -o $(PCP_USER) -g $(PCP_GROUP) -d $(PCP_SYSCONF_DIR)/pmie + $(INSTALL) -m 664 -o $(PCP_USER) -g $(PCP_GROUP) control $(PCP_PMIECONTROL_PATH) + $(INSTALL) -m 755 pmie_check.sh $(PCP_BINADM_DIR)/pmie_check$(SHELLSUFFIX) + $(INSTALL) -m 755 pmie_daily.sh $(PCP_BINADM_DIR)/pmie_daily$(SHELLSUFFIX) + $(INSTALL) -m 755 pmie2col.sh $(PCP_BIN_DIR)/pmie2col$(SHELLSUFFIX) + $(INSTALL) -m 755 rc_pmie $(PCP_RC_DIR)/pmie +ifeq ($(ENABLE_SYSTEMD),true) + $(INSTALL) -m 644 pmie.service $(PCP_SYSTEMDUNIT_DIR)/pmie.service +endif + $(INSTALL) -m 775 -o $(PCP_USER) -g $(PCP_GROUP) -d $(PCP_LOG_DIR)/pmie + $(INSTALL) -m 775 -o $(PCP_USER) -g $(PCP_GROUP) -d $(PCP_TMP_DIR)/pmie +ifeq ($(TARGET_OS),linux) + $(INSTALL) -m 755 -d `dirname $(CRONTAB_PATH)` +endif + $(INSTALL) -m 644 crontab $(CRONTAB_PATH) + +include $(BUILDRULES) + +default_pcp : default + +install_pcp : install + +pmie.service : pmie.service.in + $(SED) -e 's;@path@;'$(PCP_RC_DIR)';' $< > $@ + +crontab: crontab.in + $(SED) -e 's;@user@;'$(CRONTAB_USER)';' -e 's;@path@;'$(PCP_BINADM_DIR)';g' $< > $@ diff --git a/src/pmie/config.default b/src/pmie/config.default new file mode 100644 index 0000000..2d92240 --- /dev/null +++ b/src/pmie/config.default @@ -0,0 +1,20 @@ +// pmieconf-pmie 1 /var/pcp/config/pmieconf +// end +// +// Note: only make changes to this file after END GENERATED SECTION +// +// --- START GENERATED SECTION (do not change this section) --- +// generated by pmieconf on: Wed Jul 12 10:10:20 2000 +// + +// 1 cpu.load_average +delta = 2 min; +cpu.load_average = +some_host ( + ( kernel.all.load #'1 minute' ) + > hinv.ncpu * 3 && + ( kernel.all.load #'1 minute' ) + > 4 +) -> syslog 10 min "High 1-minute load average" " %vload@%h"; + +// --- END GENERATED SECTION (changes below will be preserved) --- diff --git a/src/pmie/control b/src/pmie/control new file mode 100644 index 0000000..2dcfd1f --- /dev/null +++ b/src/pmie/control @@ -0,0 +1,46 @@ +# +# PCP inference engine configuration/control +# +# This file is used by a number of the PCP inference engine administrative +# tools to perform maintenance on the pmie instances running on the local host. +# +# This file contains one line per host to be monitored, fields are +# Host name of host to be monitored +# S(ocks)? should this pmie be launched with pmsocks? y or n +# Log File full pathname to file where pmie activity log is to be +# maintained ... note all scripts "cd" the directory housing +# this file as a first step +# Arguments optional additional arguments to pmie +# + + +# === VARIABLE ASSIGNMENTS === +# +$version=1.0 + +# if pmsocks is being used, edit the IP address for $SOCKS_SERVER and +# uncomment the next line +#$SOCKS_SERVER=123.456.789.123; export SOCKS_SERVER + +# if remote pmie instances are run over a WAN with potentially long delays, +# adjust the following and uncomment +#$PMCD_CONNECT_TIMEOUT=20; export PMCD_CONNECT_TIMEOUT +#$PMCD_REQUEST_TIMEOUT=15; export PMCD_REQUEST_TIMEOUT + + +# === PMIE CONTROL SPECIFICATIONS === +# +# Note: - if multiple pmie instances for the same host, then they MUST use +# different log files; +# - any occurence of LOCALHOSTNAME will be replaced by local hostname; +# - pmie's configuration file search path is "./:$PCP_SYSCONF_DIR/pmie/", +# and the working directory ('.') is the dirname of the Log File. +# +#Host S? Log File Arguments +LOCALHOSTNAME n PCP_LOG_DIR/pmie/LOCALHOSTNAME/pmie.log -c config.default + +# remote host +#remote n PCP_LOG_DIR/pmie/remote/pmie.log -c config.remote + +# thru the firewall via socks +#distant y PCP_LOG_DIR/pmie/distant/pmie.log -c config.distant diff --git a/src/pmie/crontab.in b/src/pmie/crontab.in new file mode 100644 index 0000000..90d96af --- /dev/null +++ b/src/pmie/crontab.in @@ -0,0 +1,8 @@ +# +# Performance Co-Pilot crontab entries for a monitored site +# with one or more pmie instances running +# +# daily processing of pmie logs (with compression enabled) +08 0 * * * @user@ @path@/pmie_daily -X xz -x 3 +# every 30 minutes, check pmie instances are running +28,58 * * * * @user@ @path@/pmie_check -C diff --git a/src/pmie/examples/GNUmakefile b/src/pmie/examples/GNUmakefile new file mode 100644 index 0000000..f4db24f --- /dev/null +++ b/src/pmie/examples/GNUmakefile @@ -0,0 +1,91 @@ +# +# Copyright (c) 2000-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 + +UAG_SOURCE = uag.head $(sort $(wildcard uag.[0-9][0-9])) +UPM_SOURCE = upm.head $(sort $(wildcard upm.[0-9][0-9])) +CPU_SOURCE = cpu.head $(sort $(wildcard cpu.[0-9][0-9])) +DISK_SOURCE = disk.head $(sort $(wildcard disk.[0-9][0-9])) +FILESYS_SOURCE = filesys.head $(sort $(wildcard filesys.[0-9][0-9])) +RAS_SOURCE = ras.head $(sort $(wildcard ras.[0-9][0-9])) +SWAP_SOURCE = swap.head $(sort $(wildcard swap.[0-9][0-9])) +NETWORK_SOURCE = network.head $(sort $(wildcard network.[0-9][0-9])) +ENVIRON_SOURCE = environ.head $(sort $(wildcard environ.[0-9][0-9])) +WEBREPORT_SOURCE= webreport.head $(sort $(wildcard webreport.[0-9][0-9])) + +TARGETS = UAG UPM CPU DISK FILESYS RAS SWAP NETWORK ENVIRON \ + WEBREPORT + +LDIRT = $(TARGETS) + +LSRCFILES = README $(UAG_SOURCE) $(UPM_SOURCE) $(CPU_SOURCE) \ + $(DISK_SOURCE) $(FILESYS_SOURCE) $(RAS_SOURCE) $(SWAP_SOURCE) \ + $(NETWORK_SOURCE) $(ENVIRON_SOURCE) $(WEBREPORT_SOURCE) +EX_DIR = $(PCP_SHARE_DIR)/examples/pmie + +default: $(TARGETS) README + +install: default + $(INSTALL) -m 755 -d $(EX_DIR) + $(INSTALL) -m 644 $(TARGETS) README $(EX_DIR) + +UAG: $(UAG_SOURCE) + rm -f UAG + for file in $(UAG_SOURCE); do cat $$file >>UAG; echo >>UAG; done + +UPM: $(UPM_SOURCE) + rm -f UPM + for file in $(UPM_SOURCE); do cat $$file >>UPM; echo >>UPM; done + +CPU: $(CPU_SOURCE) + rm -f CPU + for file in $(CPU_SOURCE); do cat $$file >>CPU; echo >>CPU; done + +DISK: $(DISK_SOURCE) + rm -f DISK DISK.in + for file in $(DISK_SOURCE); do cat $$file >>DISK.in; echo >>DISK.in; done + sed -e "s@/usr/pcp/bin/pmpost@$(PCP_BINADM_DIR)/pmpost@" <DISK.in >DISK + rm -f DISK.in + +FILESYS: $(FILESYS_SOURCE) + rm -f FILESYS + for file in $(FILESYS_SOURCE); do cat $$file >>FILESYS; echo >>FILESYS; done + +RAS: $(RAS_SOURCE) + rm -f RAS + for file in $(RAS_SOURCE); do cat $$file >>RAS; echo >>RAS; done + +SWAP: $(SWAP_SOURCE) + rm -f SWAP + for file in $(SWAP_SOURCE); do cat $$file >>SWAP; echo >>SWAP; done + +NETWORK: $(NETWORK_SOURCE) + rm -f NETWORK + for file in $(NETWORK_SOURCE); do cat $$file >>NETWORK; echo >>NETWORK; done + +ENVIRON: $(ENVIRON_SOURCE) + rm -f ENVIRON + for file in $(ENVIRON_SOURCE); do cat $$file >>ENVIRON; echo >>ENVIRON; done + +WEBREPORT: $(WEBREPORT_SOURCE) + rm -f WEBREPORT + for file in $(WEBREPORT_SOURCE); do cat $$file >>WEBREPORT; echo >>WEBREPORT; done + +include $(BUILDRULES) + +default_pcp : default + +install_pcp : install diff --git a/src/pmie/examples/README b/src/pmie/examples/README new file mode 100644 index 0000000..1bdf4a6 --- /dev/null +++ b/src/pmie/examples/README @@ -0,0 +1,27 @@ +Example Expressions and Rules for pmie(1) + +The files in this directory contain a number of sample rules +for the Performance Co-Pilot inference engine pmie(1). + +In some cases the rules could be used directly. In others the +rules would require customization (host names, thresholds, rule +evaluation frequency, choose a suitable alarm/action, etc) before +they could be used at a particular site. + +Each file contains a set of related rules as follows: + +CPU general CPU utilization and saturation monitoring + +DISK general disk utilization and saturation monitoring + +FILESYS general file system monitoring + +RAS simple reliability and availability monitoring + (see also the shping PMDA, pmdashping(1)) + +UAG examples from the Performance Co-Pilot User's and + Administrator's Guide + +UPM examples from the Performance Co-Pilot Programmer's + Guide + diff --git a/src/pmie/examples/cpu.00 b/src/pmie/examples/cpu.00 new file mode 100644 index 0000000..403dfa4 --- /dev/null +++ b/src/pmie/examples/cpu.00 @@ -0,0 +1,9 @@ +// +// Unusual usr-sys split when some CPU is more than 20% in usr mode +// and sys mode is at least 1.5 times usr mode +// +cpu_usr_sys = + some_inst ( + $percpu.cpu.sys > $percpu.cpu.user * 1.5 && $percpu.cpu.user > 0.2 + ) + -> alarm "Unusual sys time: " "%i "; diff --git a/src/pmie/examples/cpu.01 b/src/pmie/examples/cpu.01 new file mode 100644 index 0000000..799aa99 --- /dev/null +++ b/src/pmie/examples/cpu.01 @@ -0,0 +1,15 @@ +// +// Over all CPUs, syscall_rate > 1000 * no_of_cpus +// +cpu_syscall = + $all.syscall > 1000 count/sec * hinv.ncpu + -> print "high aggregate syscalls: %v"; + +// Sustained high syscall rate on a single CPU +// +delta = 30 sec; +percpu_syscall = + some_inst ( + $percpu.syscall > 2000 count/sec + ) + -> syslog "Sustained syscalls per second? " "[%i] %v "; diff --git a/src/pmie/examples/cpu.02 b/src/pmie/examples/cpu.02 new file mode 100644 index 0000000..d5b1fbb --- /dev/null +++ b/src/pmie/examples/cpu.02 @@ -0,0 +1,10 @@ +// +// the 1 minute load average exceeds 5 * number of CPUs on any host +// + +hosts = ":gonzo :moomba"; // change as required +delta = 1 minute; // no need to evaluate more often than this +high_load = + some_host ( + $all.load $hosts #'1 minute' > 5 * hinv.ncpu $hosts + ) -> alarm "High Load Average? " "%h: %v "; diff --git a/src/pmie/examples/cpu.head b/src/pmie/examples/cpu.head new file mode 100644 index 0000000..52a493f --- /dev/null +++ b/src/pmie/examples/cpu.head @@ -0,0 +1,12 @@ +// +// Some Common Performance Monitoring Scenarios +// +// The CPU Group +// + +delta = 2 sec; // more often for demonstration purposes + +// common prefixes +// +percpu = "kernel.percpu"; +all = "kernel.all"; diff --git a/src/pmie/examples/disk.00 b/src/pmie/examples/disk.00 new file mode 100644 index 0000000..a69a8d7 --- /dev/null +++ b/src/pmie/examples/disk.00 @@ -0,0 +1,28 @@ +// +// Any disk performing more than 40 I/Os per second, sustained over +// at least 30 seconds is probably busy +// +delta = 30 seconds; +disk_busy = + some_inst ( + $disk.dev.total > 40 count/sec + ) + -> shell 15 mins "Mail -s 'Heavy sustained disk traffic' sysadm </dev/null"; + +// Try and catch bursts of activity ... more than 60 I/Os per second +// for at least 25% of 8 consecutive 3 second samples +// +delta = 3 sec; +disk_burst = + some_inst ( + 25%_sample ( + $disk.dev.total @0..7 > 60 count/sec + ) + ) + -> alarm 5 mins "Disk Burst? " "%i "; + +// any SCSI disk controller performing more than 3 Mbytes per sec is busy +// +some_inst $disk.ctl.blktotal * 0.5 > 3 Mbyte/sec + -> alarm "Busy Disk Controller: " "%i "; + diff --git a/src/pmie/examples/disk.10 b/src/pmie/examples/disk.10 new file mode 100644 index 0000000..9cbf50d --- /dev/null +++ b/src/pmie/examples/disk.10 @@ -0,0 +1,23 @@ +// +// A subset of the disks on a particular host are either busy +// (more than 30 I/Os per second averaged over these disks) or one +// disk is busy (more than 50 I/Os per second) with write-dominated +// (more than 75%) activity + +delta = 10 sec; + +myhost = "moomba"; // the host of interest +mydisks = "#dks1d1 #dks1d2 #dks3d2"; // the disks of interest on this host + +metric = "disk.dev"; + +disk_group_busy = + ( + avg_inst ( $metric.total :$myhost $mydisks ) > 10 count/sec || + some_inst ( + $metric.total :$myhost $mydisks > 50 count/sec && + $metric.write :$myhost $mydisks > + 3 * $metric.write :$myhost $mydisks + ) + ) + -> alarm "Busy disks: $mydisks on host: $myhost)"; diff --git a/src/pmie/examples/disk.20 b/src/pmie/examples/disk.20 new file mode 100644 index 0000000..ec086e5 --- /dev/null +++ b/src/pmie/examples/disk.20 @@ -0,0 +1,13 @@ +// +// Assume the / and /usr file systems are on different partitions +// of the same disk (/dev/dsk0d1 in the example below). +// Add an entry to the file $PCP_LOG_DIR/NOTICES when this disk is +// busy and either of the file systems is more than 90% full. +// +// Suggestion from: Steve Daniels (steve@houdini.denver.sgi.com) + +delta = 60; + +( filesys.full #'/dev/root' > 90 || filesys.full #'/dev/usr' > 90 ) +&& disk.dev.total #'dks0d1' > 40 count/sec + -> shell 15min "/usr/pcp/bin/pmpost 'dks0d1 busy when / or /usr nearly full'"; diff --git a/src/pmie/examples/disk.head b/src/pmie/examples/disk.head new file mode 100644 index 0000000..20f133e --- /dev/null +++ b/src/pmie/examples/disk.head @@ -0,0 +1,11 @@ +// +// Some Common Performance Monitoring Scenarios +// +// The Disk Group +// + +delta = 15 sec; // often enough for disks? + +// common prefixes +// +disk = "disk"; diff --git a/src/pmie/examples/environ.00 b/src/pmie/examples/environ.00 new file mode 100644 index 0000000..85e5597 --- /dev/null +++ b/src/pmie/examples/environ.00 @@ -0,0 +1,18 @@ +// +// Absolute temperature ceiling. +// +// Rules donated by Kevin Wang at Silicon Graphics +// + +some_host ( environ.temp $HOSTS > 33 ) +-> print 10 min "absolute temperature alarm! " "%h: %v degrees "; + +// +// Watch the machine room temperature. If it rises more than 2 degrees +// every $delta, danger! +// This is different from the absolute rule above ... this one +// gives early warning of sustained temperature increases. +// +some_host ( + environ.temp $HOSTS @0 - environ.temp $HOSTS @1 > 2 +) -> print "temperature rise alarm: " "%h: %v degree rise in $DELTA_STR "; diff --git a/src/pmie/examples/environ.head b/src/pmie/examples/environ.head new file mode 100644 index 0000000..78936b9 --- /dev/null +++ b/src/pmie/examples/environ.head @@ -0,0 +1,17 @@ +// +// Some Common Performance Monitoring Scenarios +// +// The Environ Group +// +// Note: need environ PMDA installed on a Challenge L or XL for +// required metrics to be available + +// replace with your hosts +HOSTS = ":localhost :foo"; + +// replace this with your e-mail address +MINDER = "root@localhost"; + +// 1 minute rulesets in this group +delta = 1 min; // numbers are diff than strings +DELTA_STR = "1 minute"; // strings are diff than numbers diff --git a/src/pmie/examples/filesys.00 b/src/pmie/examples/filesys.00 new file mode 100644 index 0000000..937e123 --- /dev/null +++ b/src/pmie/examples/filesys.00 @@ -0,0 +1,14 @@ +// +// Either the /tmp or the /usr filesystem being +// more than 95% full +// + +delta = 5 mins; // often enough for file system fullness? + +tmp_full = + $fsys.free #'/dev/root' / $fsys.capacity #'/dev/root' < 0.05 + -> syslog "/dev/root filesystem (almost) full"; + +usr_full = + $fsys.free #'/dev/usr' / $fsys.capacity #'/dev/usr' < 0.05 + -> syslog "/dev/usr filesystem (almost) full"; diff --git a/src/pmie/examples/filesys.10 b/src/pmie/examples/filesys.10 new file mode 100644 index 0000000..2cf9488 --- /dev/null +++ b/src/pmie/examples/filesys.10 @@ -0,0 +1,14 @@ +// +// Some read activity through the buffer cache and the cache read +// hit ratio is less than 80% +// (lots of file system reads causing physical I/O) +// + +delta = 1 min; // check every minute + +blkio = "kernel.all.io"; +poor_read_hits = + (($blkio.lread - $blkio.bread) / $blkio.lread) < 0.8 && $blkio.lread > 100 + -> alarm 20 min "poor buffer cache read hit ratio (%v)"; + // Note: %v in alarm string is bound to the left most + // expression in the predicate diff --git a/src/pmie/examples/filesys.20 b/src/pmie/examples/filesys.20 new file mode 100644 index 0000000..856d8ed --- /dev/null +++ b/src/pmie/examples/filesys.20 @@ -0,0 +1,14 @@ +// +// at least $threshold full and at the current rate of growth will fill +// the file system in less than $lead_time +// ie. used + $lead_time * growth-rate > capacity + +delta = 1 min; // check every minute +threshold = 40; // must be at least this full now (percentage) +lead_time = "15min"; // lead time before the filesystem will be full + +some_inst ( + 100 * filesys.used / filesys.capacity > $threshold && + filesys.used + $lead_time * ( rate filesys.used ) > + filesys.capacity +) -> print "filesystem will be full within $lead_time:" " %i"; diff --git a/src/pmie/examples/filesys.head b/src/pmie/examples/filesys.head new file mode 100644 index 0000000..de4c703 --- /dev/null +++ b/src/pmie/examples/filesys.head @@ -0,0 +1,10 @@ +// +// Some Common Performance Monitoring Scenarios +// +// The File System Group +// + +// common prefixes +// +fsys = "filesys"; + diff --git a/src/pmie/examples/network.00 b/src/pmie/examples/network.00 new file mode 100644 index 0000000..44c2183 --- /dev/null +++ b/src/pmie/examples/network.00 @@ -0,0 +1,10 @@ +// +// Report when some interface has seen more than 15 errors per second +// on at least 3 of the last 4 observations +// +// Rule donated by Kevin Wang at Silicon Graphics +// + +some_host some_inst 75%_sample ( + network.interface.total.errors $HOSTS @0..3 > 15 +) -> print 5 min "high network interface errors" "%h[%i] %v errors/sec "; diff --git a/src/pmie/examples/network.head b/src/pmie/examples/network.head new file mode 100644 index 0000000..909e773 --- /dev/null +++ b/src/pmie/examples/network.head @@ -0,0 +1,15 @@ +// +// Some Common Performance Monitoring Scenarios +// +// The Network Group +// + +// replace with your hosts +HOSTS = ":localhost :foo"; + +// replace this with your e-mail address +MINDER = "root@localhost"; + +// 10 second rulesets in this group +delta = 10 sec; // numbers are diff than strings +DELTA_STR = "10 seconds"; // strings are diff than numbers diff --git a/src/pmie/examples/ras.00 b/src/pmie/examples/ras.00 new file mode 100644 index 0000000..930b964 --- /dev/null +++ b/src/pmie/examples/ras.00 @@ -0,0 +1,11 @@ +// +// For Origin systems, sequence number errors are not indicative of +// a problem, but persistent checkbit and/or retry errors may indicate +// a CrayLink interconnect problem. +// +some_inst ( all_sample ( + hw.router.perport.cb_errors @0..2 > 0 || + hw.router.perport.retry_errors @0..2 > 0 +) ) + -> alarm 30mins "CrayLink SN and/or Retry errors: " "%i "; + diff --git a/src/pmie/examples/ras.head b/src/pmie/examples/ras.head new file mode 100644 index 0000000..d571913 --- /dev/null +++ b/src/pmie/examples/ras.head @@ -0,0 +1,5 @@ +// +// Some System Reliability, Availability and Serviceability (RAS) Checks +// + +delta = 20 sec; diff --git a/src/pmie/examples/swap.00 b/src/pmie/examples/swap.00 new file mode 100644 index 0000000..2e3f309 --- /dev/null +++ b/src/pmie/examples/swap.00 @@ -0,0 +1,23 @@ +// +// report when swap > 50-75% full and when swap > 75% full +// +// Rules donated by Kevin Wang at Silicon Graphics +// +// note: the sort hack '9999999' to keep the header first; later +// removed by sed +// note: -o option to ps(1) requires IRIX 6.2 or later ... for IRIX 5.3 +// this would have to be re-written using ps -el + +SWAP="swap"; +some_host ( + ($SWAP.free $HOSTS / $SWAP.length $HOSTS) * 100 < 50 && + ($SWAP.free $HOSTS / $SWAP.length $HOSTS) * 100 >= 25 +) -> print 10 min "swap more than half-full: " "%h: %v% free " & + shell 10 min "rsh -n guest@%h /sbin/ps -eo 'ruser=UID,pid=PID,ppid=PPID,pcpu=%CPU,sz=9999999SZ,rss=RSS,stime=STIME,time=TIME,args=CMD' | sort +4 -nr | sed -e 's/9999999SZ / SZ:/' | /usr/sbin/Mail -s '%h swap more than half-full (%v% free)' $MINDER &"; + +some_host ( + ($SWAP.free $HOSTS / $SWAP.length $HOSTS) * 100 < 25 +) -> print 10 min "swap almost full: " "%h: %v% free " & + shell 10 min "rsh -n guest@%h /sbin/ps -eo 'ruser=UID,pid=PID,ppid=PPID,pcpu=%CPU,sz=9999999SZ,rss=RSS,stime=STIME,time=TIME,args=CMD' | sort +4 -nr | sed -e 's/9999999SZ / SZ:/' | /usr/sbin/Mail -s '%h swap almost full (%v% free)' $MINDER &"; + + diff --git a/src/pmie/examples/swap.head b/src/pmie/examples/swap.head new file mode 100644 index 0000000..254cf4a --- /dev/null +++ b/src/pmie/examples/swap.head @@ -0,0 +1,13 @@ +// +// The Swap Group +// + +// replace with your hosts +HOSTS = ":localhost :foo"; + +// replace this with your e-mail address +MINDER = "root@localhost"; + +// 20 second rulesets in this group +delta = 20 sec; // numbers are diff than strings +DELTA_STR = "20 seconds"; // strings are diff than numbers diff --git a/src/pmie/examples/uag.00 b/src/pmie/examples/uag.00 new file mode 100644 index 0000000..6cb9638 --- /dev/null +++ b/src/pmie/examples/uag.00 @@ -0,0 +1,5 @@ +// +// a simple expression, with multiple values +// +iops = disk.dev.total; + diff --git a/src/pmie/examples/uag.01 b/src/pmie/examples/uag.01 new file mode 100644 index 0000000..7e6d691 --- /dev/null +++ b/src/pmie/examples/uag.01 @@ -0,0 +1,4 @@ +// +// total disk write percentage +// +wrt_pct = (disk.all.write / disk.all.total) * 100; diff --git a/src/pmie/examples/uag.02 b/src/pmie/examples/uag.02 new file mode 100644 index 0000000..6127209 --- /dev/null +++ b/src/pmie/examples/uag.02 @@ -0,0 +1,8 @@ +// +// some varied expressions +// +pct_wrt = (disk.all.write / disk.all.total) * 100; +busy_wrt = disk.dev.total > 10 && + disk.dev.write > disk.dev.read; +busy = some_inst disk.dev.total > 60 -> print "[%i] high disk i/o "; + diff --git a/src/pmie/examples/uag.03 b/src/pmie/examples/uag.03 new file mode 100644 index 0000000..01e4d7c --- /dev/null +++ b/src/pmie/examples/uag.03 @@ -0,0 +1,7 @@ +// +// simple use of a macro +// +disk = "disk.all"; +pct_wrt = ($disk.write / $disk.total) * 100; + + diff --git a/src/pmie/examples/uag.04 b/src/pmie/examples/uag.04 new file mode 100644 index 0000000..e13a5ef --- /dev/null +++ b/src/pmie/examples/uag.04 @@ -0,0 +1,52 @@ +// +// perverse example to show all possible choices of units for numeric +// constants +// +mem.freemem > 1 byte; +mem.freemem > 1 Kbyte; +mem.freemem > 1 Mbyte; +mem.freemem > 1 Gbyte; +mem.freemem > 1 Tbyte; + +disk.dev.blktotal > 1 Mbyte / nsec; +disk.dev.blktotal > 1 Mbyte / nanosecond; +disk.dev.blktotal > 1 Mbyte / usec; +disk.dev.blktotal > 1 Mbyte / microsecond; +disk.dev.blktotal > 1 Mbyte / msec; +disk.dev.blktotal > 1 Mbyte / millisecond; +disk.dev.blktotal > 1 Mbyte / sec; +disk.dev.blktotal > 1 Mbyte / second; +disk.dev.blktotal > 1 Mbyte / min; +disk.dev.blktotal > 1 Mbyte / minute; +disk.dev.blktotal > 1 Mbyte / hour; + +hinv.ncpu > 1 count; +hinv.ncpu > 1 Kcount; +hinv.ncpu > 1 count; +hinv.ncpu > 1 Gcount; +hinv.ncpu > 1 Tcount; + +mem.freemem > 1 bytes; +mem.freemem > 1 Kbytes; +mem.freemem > 1 Mbytes; +mem.freemem > 1 Gbytes; +mem.freemem > 1 Tbytes; + +disk.dev.blktotal > 1 Mbyte / nsecs; +disk.dev.blktotal > 1 Mbyte / nanoseconds; +disk.dev.blktotal > 1 Mbyte / usecs; +disk.dev.blktotal > 1 Mbyte / microseconds; +disk.dev.blktotal > 1 Mbyte / msecs; +disk.dev.blktotal > 1 Mbyte / milliseconds; +disk.dev.blktotal > 1 Mbyte / secs; +disk.dev.blktotal > 1 Mbyte / seconds; +disk.dev.blktotal > 1 Mbyte / mins; +disk.dev.blktotal > 1 Mbyte / minutes; +disk.dev.blktotal > 1 Mbyte / hours; + +hinv.ncpu > 1 counts; +hinv.ncpu > 1 Kcounts; +hinv.ncpu > 1 counts; +hinv.ncpu > 1 Gcounts; +hinv.ncpu > 1 Tcounts; + diff --git a/src/pmie/examples/uag.10 b/src/pmie/examples/uag.10 new file mode 100644 index 0000000..7135f6b --- /dev/null +++ b/src/pmie/examples/uag.10 @@ -0,0 +1,32 @@ +// +// metric expressions + +// all instances +// +enet = network.interface.total.packets; + +// restricted instance (loopback interface only) +// +enet_r = network.interface.total.packets #lo0; + +// restricted instances with weird instance names ... +// note instance names are "identifiers" in the grammar, so single +// quotes required for tricky characters, like /, spaces, etc, _not_ +// double quotes +// +root_n_usr = filesys.free #'/dev/root' #'/dev/usr'; + +// multiple hosts +// +num_cpu = hinv.ncpu :babylon.engr.sgi.com :gonzo :sandpit; + +// multiple sample times +// +mem_trend = mem.freemem @0..3; + +// multi-dimension variations +// + +// missing instance for non-singular instance domain, plus multiple hosts +// +net_view = network.interface.total.packets :gonzo :moomba; diff --git a/src/pmie/examples/uag.11 b/src/pmie/examples/uag.11 new file mode 100644 index 0000000..7d4b86b --- /dev/null +++ b/src/pmie/examples/uag.11 @@ -0,0 +1,7 @@ +// +// relational (logical) expressions +// +hosts = ":gonzo"; +intfs = "#ec0 #ec2"; +all_intf = network.interface.in.packets + $hosts $intfs @0..2 > 300 count/sec; diff --git a/src/pmie/examples/uag.12 b/src/pmie/examples/uag.12 new file mode 100644 index 0000000..3850168 --- /dev/null +++ b/src/pmie/examples/uag.12 @@ -0,0 +1,12 @@ +// +// quantification examples +// + +// some_instance +all_intf = network.interface.in.packets + #ec0 #ec2 @0..2 > 300 count/sec; +any_sample = some_sample + network.interface.in.packets + #ec0 #ec2 @0..2 > 300 count/sec; + + diff --git a/src/pmie/examples/uag.13 b/src/pmie/examples/uag.13 new file mode 100644 index 0000000..9990407 --- /dev/null +++ b/src/pmie/examples/uag.13 @@ -0,0 +1,33 @@ +// +// nested quantification +// + +Servers = ":moomba :gonzo"; // change as desired + +// read and write rate per disk per host +// +rd = disk.dev.read $Servers; +wr = disk.dev.write $Servers; + +// one value per host, true if 20% or more of the disks are doing +// significant reading or writing +// +rd_20 = 20%_inst disk.dev.read $Servers > 40; +wr_20 = 20%_inst disk.dev.write $Servers > 40; + +// single truth value: more than 20% of the disks busy reading or writing +// on all hosts? +// +summary = all_host ( + 20%_inst disk.dev.read $Servers > 40 || + 20%_inst disk.dev.write $Servers > 40 + ); + +// alternate form +// +summary2 = all_host ( + 20%_inst ( + disk.dev.read $Servers > 40 || + disk.dev.write $Servers > 40 + ) + ); diff --git a/src/pmie/examples/uag.20 b/src/pmie/examples/uag.20 new file mode 100644 index 0000000..475017c --- /dev/null +++ b/src/pmie/examples/uag.20 @@ -0,0 +1,7 @@ +// +// a rule expression with multiple actions and %-binding in the +// arguments for the action methods +// +some_inst ( disk.dev.total > 60 ) + -> syslog 10 mins "[%i] busy, %v IOPS " & + shell 1 hour "echo 'Disk %i is REALLY busy. Running at %v I/Os per second' | Mail -s 'pmie alarm' sysadm"; diff --git a/src/pmie/examples/uag.21 b/src/pmie/examples/uag.21 new file mode 100644 index 0000000..c841ff2 --- /dev/null +++ b/src/pmie/examples/uag.21 @@ -0,0 +1,9 @@ +// +// a rule expression with multiple actions and %-binding in the +// arguments for the action methods ... use some creative string +// composition for the final message +// +some_inst ( disk.dev.total > 50 ) + -> syslog 10 mins "Busy disks: " "%i @ %v IOPS " & + shell 1 hour "echo 'REALLY busy disks: " "%i @ %v I/Os per second " "' | Mail -s 'pmie alarm' sysadm"; + diff --git a/src/pmie/examples/uag.30 b/src/pmie/examples/uag.30 new file mode 100644 index 0000000..b15a781 --- /dev/null +++ b/src/pmie/examples/uag.30 @@ -0,0 +1,17 @@ +// +// intrinsic operators +// + +m = mem.freemem; +rate_m = rate mem.freemem; + +// At least 2 CPUs doing some reasonable amount of work +// +poke = ":moomba :'mac-larry' :bitbucket"; // note '' to escape - in host name +u = kernel.percpu.cpu.user $poke; +s = kernel.percpu.cpu.sys $poke; +some_host ( + count_inst ( kernel.percpu.cpu.user $poke + + kernel.percpu.cpu.sys $poke > 0.7 ) >= 2 + ) + -> alarm "2 or more busy CPUs"; diff --git a/src/pmie/examples/uag.head b/src/pmie/examples/uag.head new file mode 100644 index 0000000..9ba4f40 --- /dev/null +++ b/src/pmie/examples/uag.head @@ -0,0 +1,3 @@ +// +// Examples from the Performance Co-Pilot User's and Administrator's Guide +// diff --git a/src/pmie/examples/upm.00 b/src/pmie/examples/upm.00 new file mode 100644 index 0000000..1b9e04c --- /dev/null +++ b/src/pmie/examples/upm.00 @@ -0,0 +1,6 @@ +// +// If the total context switch rate exceeds 10000 per second per CPU +// then display an alarm notifier +// +kernel.all.pswitch / hinv.ncpu > 10000 count/sec + -> alarm "high context switch rate %v"; diff --git a/src/pmie/examples/upm.01 b/src/pmie/examples/upm.01 new file mode 100644 index 0000000..d83bd79 --- /dev/null +++ b/src/pmie/examples/upm.01 @@ -0,0 +1,4 @@ +all_sample ( + kernel.all.pswitch @0..9 > 10 Kcount/sec * hinv.ncpu +) -> shell 5 min "xwsh -e 'top'"; + diff --git a/src/pmie/examples/upm.02 b/src/pmie/examples/upm.02 new file mode 100644 index 0000000..eaa0c42 --- /dev/null +++ b/src/pmie/examples/upm.02 @@ -0,0 +1,19 @@ +delta = 5 sec; // force evaluation once every 5 seconds from here on + +// If for any disk, for all 4 samples (20 seconds), the disk is performing +// more than 40 I/Os per second, then print a message to standard output and +// then launch dkvis(1) +// +some_inst all_sample + disk.dev.total @0..3 > 40 count/sec + -> print "disks busy for 20 sec:" " %i" & + shell 5 min "dkvis"; + +// If any disk is performing more than 60 I/Os per second, then +// print a message identifying the busy disk to standard output and +// launch dkvis(1) +some_inst ( + disk.dev.total > 60 count/sec +) -> print "busy disks:" " %i" & + shell 5 min "dkvis"; + diff --git a/src/pmie/examples/upm.03 b/src/pmie/examples/upm.03 new file mode 100644 index 0000000..ff7501e --- /dev/null +++ b/src/pmie/examples/upm.03 @@ -0,0 +1,8 @@ +// +// Refine the preceding rule to apply only between the hours of 9am and 5pm, +// and to require that just 3 of the four samples exceed the threshold +// +$hour >= 9 && $hour <= 17 && some_inst 75 %_sample + disk.dev.total @0..3 > 40 count/sec + -> print "disk busy for 20 sec" & + shell 5 min "dkvis"; diff --git a/src/pmie/examples/upm.04 b/src/pmie/examples/upm.04 new file mode 100644 index 0000000..36c9699 --- /dev/null +++ b/src/pmie/examples/upm.04 @@ -0,0 +1,10 @@ +// +// Refine the preceding rule further to print the host name and disk name +// for which the threshold is exceeded +// +$hour >= 9 && $hour <= 17 && +some_inst ( + 75 %_sample ( + disk.dev.total @0..3 > 40 count/sec + ) +) -> print "disks busy for 20 sec:" " [%h]%i"; diff --git a/src/pmie/examples/upm.05 b/src/pmie/examples/upm.05 new file mode 100644 index 0000000..702747e --- /dev/null +++ b/src/pmie/examples/upm.05 @@ -0,0 +1,9 @@ +// +// Macro for use ... +// +bc = "buffer_cache"; +// Using the above macro; If the buffer cache is in use (more than 50 read +// requests) with hit ratio less than 90%, then popup an alarm +// +$bc.getblks > 50 && $bc.getfound / $bc.getblks < 0.9 + -> alarm "poor buffer cache hit rate"; diff --git a/src/pmie/examples/upm.06 b/src/pmie/examples/upm.06 new file mode 100644 index 0000000..dbf9da9 --- /dev/null +++ b/src/pmie/examples/upm.06 @@ -0,0 +1,10 @@ +delta = 10 mins; // force evaluation once every 10 minutes from here on + +// If either the / or the /usr filesystem is more than 95% full, display +// an alarm popup, but not if it has already been displayed during the last +// 24 hours +// +filesys.free #'/dev/root' / filesys.capacity #'/dev/root' < 0.05 + -> alarm 24 hour "root filesystem (almost) full"; +filesys.free #'/dev/usr' / filesys.capacity #'/dev/usr' < 0.05 + -> alarm 24 hour "/usr filesystem (almost) full"; diff --git a/src/pmie/examples/upm.07 b/src/pmie/examples/upm.07 new file mode 100644 index 0000000..f325ee6 --- /dev/null +++ b/src/pmie/examples/upm.07 @@ -0,0 +1,8 @@ +// +// The following rule requires a machine that supports the PCP environment +// metrics. If the machine environment temperature rises more than 2 +// degrees over a 10 minute interval, write an entry in the system log +// +environ.temp @1 - environ.temp @0 > 2 + -> alarm "temperature rising fast" & + syslog "machine room temperature rise alarm"; diff --git a/src/pmie/examples/upm.08 b/src/pmie/examples/upm.08 new file mode 100644 index 0000000..8ab0e49 --- /dev/null +++ b/src/pmie/examples/upm.08 @@ -0,0 +1,13 @@ +// +// Something interesting if you have performance problems with +// your Oracle data base ... +// +db = "oracle.ptg1"; +host = ":moomba.melbourne.sgi.com"; +lru = "#'cache buffers lru chain'"; +gets = "$db.latch.gets $host $lru"; +total = "$db.latch.gets $host $lru + $db.latch.misses $host $lru + + $db.latch.immisses $host $lru"; + +$total > 100 && $gets / $total < 0.2 + -> alarm "high LRU latch contention"; diff --git a/src/pmie/examples/upm.09 b/src/pmie/examples/upm.09 new file mode 100644 index 0000000..e2f12cf --- /dev/null +++ b/src/pmie/examples/upm.09 @@ -0,0 +1,11 @@ +// Busy disk? + +delta = 20 sec; // force evaluation once every 20 seconds from here on + +// If any disk is performing more than 60 I/Os per second, then +// print a message to standard output and launch dkvis(1) +// +some_inst + disk.dev.total > 60 count/sec + -> print "disk busy for 20 sec" "%v IOPS %i@%h" & + shell 5 min "dkvis"; diff --git a/src/pmie/examples/upm.10 b/src/pmie/examples/upm.10 new file mode 100644 index 0000000..ce40c1a --- /dev/null +++ b/src/pmie/examples/upm.10 @@ -0,0 +1,11 @@ +delta = 1 minute; +ruleset + kernel.all.load #'1 minute' > 10 * hinv.ncpu -> + print "extreme load average %v" +else kernel.all.load #'1 minute' > 2 * hinv.ncpu -> + print "moderate load average %v" +unknown -> + print "load average unavailable" +otherwise -> + print "load average OK" +; diff --git a/src/pmie/examples/upm.head b/src/pmie/examples/upm.head new file mode 100644 index 0000000..3a286e9 --- /dev/null +++ b/src/pmie/examples/upm.head @@ -0,0 +1,5 @@ +// +// Examples from the pmie(1) man page +// + +delta = 1 sec; // force evaluation once per second diff --git a/src/pmie/examples/webreport.00 b/src/pmie/examples/webreport.00 new file mode 100644 index 0000000..70e04d8 --- /dev/null +++ b/src/pmie/examples/webreport.00 @@ -0,0 +1,8 @@ +// +// Request rate throughput (requests per second) summaries +// + +// you may replace the metric below with any of the other +// web.allservers.requests metrics + +request_rate = web.allservers.requests.total; diff --git a/src/pmie/examples/webreport.01 b/src/pmie/examples/webreport.01 new file mode 100644 index 0000000..cab9272 --- /dev/null +++ b/src/pmie/examples/webreport.01 @@ -0,0 +1,8 @@ +// +// Data throughput (Kbytes per minute) summaries +// + +// you may replace the metric below with any of the other +// web.allservers.bytes metrics + +data_rate = web.allservers.bytes.total * 60 / 1024; diff --git a/src/pmie/examples/webreport.head b/src/pmie/examples/webreport.head new file mode 100644 index 0000000..91deb82 --- /dev/null +++ b/src/pmie/examples/webreport.head @@ -0,0 +1,11 @@ +// +// Some Common Performance Monitoring Scenarios +// +// The WEBREPORT Group +// +// Intended to be used with archive logs to produce summaries, e.g. +// pmie -v -A 1hour -S @10:00am -T @2:00pm -a somearchive WEBREPORT +// produces hourly summaries for the hours 10am to 2pm +// + +delta = 1 hour; // change to suit, else use -t from the command line diff --git a/src/pmie/pmie.service.in b/src/pmie/pmie.service.in new file mode 100644 index 0000000..28d228b --- /dev/null +++ b/src/pmie/pmie.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=Performance Metrics Inference Engine +Documentation=man:pmie(1) +After=local-fs.target network.target + +[Service] +Type=oneshot +ExecStart=@path@/pmie start +ExecStop=@path@/pmie stop +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/src/pmie/pmie2col.sh b/src/pmie/pmie2col.sh new file mode 100755 index 0000000..fafe542 --- /dev/null +++ b/src/pmie/pmie2col.sh @@ -0,0 +1,133 @@ +#!/bin/sh +# +# A crude ascii reporting tool, convert pmie to column output +# +# The pmie rules need to be of the form: +# load_1 = kernel.all.load #'1 minute'; +# idle = kernel.all.cpu.idle; +# column_name=some other expression; +# ... +# +# Each pmie expression has to produce a singular value. +# +# With timestamps (pmie -e or pmie output from a PCP archive), lines look like +# metric (Tue Feb 13 05:01:19 2001): value +# load_1 (Tue Dec 23 12:20:45 2003): 0.24 +# the first sed step in the filter sorts this out. +# +# First e-mailed to Patrick Aland <paland@stetson.edu> and pcp@oss.sgi.com +# on Wed, 24 Jan 2001. +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +status=1 +trap "rm -rf $tmp; exit \$status" 0 1 2 3 15 +prog=`basename $0` + +# For interactive use, works better with line buffered output from sed(1) +# and awk(1). +# +case "$PCP_PLATFORM" +in + linux|mingw|kfreebsd|gnu) + SED="sed -u" + ;; + freebsd|darwin) + SED="sed -l" + ;; + *) + SED=sed + ;; +esac + +echo > $tmp/usage +cat >> $tmp/usage <<EOF +Options: + -d=CHAR,--delimiter=CHAR set the output delimiter character + -p=N,--precision=N set the output floating point precision + -w=N,--width=N set the width of each column of output + --help +EOF + +_usage() +{ + pmgetopt --progname=$prog --config=$tmp/usage --usage + exit 1 +} + +pre=2 # floating point precision +wid=7 # reporting column width +delim=' ' + +ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"` +[ $? != 0 ] && exit 1 + +eval set -- "$ARGS" +while [ $# -gt 0 ] +do + case "$1" + in + -w) wid="$2" + shift + ;; + -p) pre="$2" + shift + ;; + -d) delim="$2" + shift + ;; + --) shift + break + ;; + -\?) _usage + ;; + esac + shift +done + +[ $# -eq 0 ] || _usage + +# culled output at the start is produced by pmie and/or pmafm +$SED \ + -e '/^pmie: timezone set to/d' \ + -e '/^Note: running pmie serially, once per archive$/d' \ + -e '/^Host: [A-Za-z0-9]* Archive: /d' \ + -e '/^[^ ][^ ]* ([A-Z][a-z][a-z] [A-Z][a-z][a-z] *[0-9][0-9]* [0-2][0-9]:[0-5][0-9]:[0-5][0-9] [0-9][0-9][0-9][0-9]): /{ +s/ (/|/ +s/): /|/ +}' \ + -e '/^\([^ ][^ ]*\):/s//\1||/' \ +| awk -F\| -v delim="$delim" ' +NF == 0 { if (state == 0) { + ncol = i + print "" + } + if (stamp != "") printf "%24s ",stamp + for (i = 0; i < ncol; i++) { + if (v[i] == "?") + # no value + printf "%s%'$wid'.'$wid's",delim,"?" + else if (v[i]+0 == v[i]) + # number + printf "%s%'$wid'.'$pre'f",delim,v[i] + else + # string + printf "%s%'$wid'.'$wid's",delim,v[i] + v[i] = "?" + } + print "" + i = 0 + stamp = "" + state = 1 + next + } +NF == 3 && stamp == "" { stamp = $2 } +state == 0 { if (i == 0 && stamp != "") printf "%24s ","" + printf "%s%'$wid'.'$wid's",delim,$1 } + { v[i++] = $NF }' + +status=0 +exit diff --git a/src/pmie/pmie_check.sh b/src/pmie/pmie_check.sh new file mode 100644 index 0000000..273903b --- /dev/null +++ b/src/pmie/pmie_check.sh @@ -0,0 +1,691 @@ +#! /bin/sh +# +# Copyright (c) 2013-2014 Red Hat. +# Copyright (c) 1998-2000,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. +# +# Administrative script to check pmie processes are alive, and restart +# them as required. +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/rc-proc.sh + +PMIE=pmie +PMIECONF="$PCP_BIN_DIR/pmieconf" + +# error messages should go to stderr, not the GUI notifiers +unset PCP_STDERR + +# added to handle problem when /var/log/pcp is a symlink, as first +# reported by Micah_Altman@harvard.edu in Nov 2001 +# +_unsymlink_path() +{ + [ -z "$1" ] && return + __d=`dirname $1` + __real_d=`cd $__d 2>/dev/null && $PWDCMND` + if [ -z "$__real_d" ] + then + echo $1 + else + echo $__real_d/`basename $1` + fi +} + +# constant setup +# +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +status=0 +echo >$tmp/lock +trap "rm -rf \`[ -f $tmp/lock ] && cat $tmp/lock\` $tmp; exit \$status" 0 1 2 3 15 +prog=`basename $0` + +# control file for pmie administration ... edit the entries in this +# file to reflect your local configuration +# +CONTROL=$PCP_PMIECONTROL_PATH + +# NB: FQDN cleanup; don't guess a 'real name for localhost', and +# definitely don't truncate it a la `hostname -s`. Instead now +# we use such a string only for the default log subdirectory, ie. +# for substituting LOCALHOSTNAME in the third column of $CONTROL. + +# determine path for pwd command to override shell built-in +PWDCMND=`which pwd 2>/dev/null | $PCP_AWK_PROG ' +BEGIN { i = 0 } +/ not in / { i = 1 } +/ aliased to / { i = 1 } + { if ( i == 0 ) print } +'` +[ -z "$PWDCMND" ] && PWDCMND=/bin/pwd +eval $PWDCMND -P >/dev/null 2>&1 +[ $? -eq 0 ] && PWDCMND="$PWDCMND -P" +here=`$PWDCMND` + +# determine whether we can automatically enable any events sinks +CONFARGS="-cF" +if which esplogger >/dev/null 2>&1 +then + CONFARGS='m global syslog_prefix $esp_prefix$' +fi + +# option parsing +# +SHOWME=false +MV=mv +RM=rm +CP=cp +KILL=pmsignal +TERSE=false +VERBOSE=false +VERY_VERBOSE=false +CHECK_RUNLEVEL=false +START_PMIE=true + +echo > $tmp/usage +cat >> $tmp/usage << EOF +Options: + -c=FILE,--control=FILE configuration of pmie instances to manage + -C query system service runlevel information + -N,--showme perform a dry run, showing what would be done + -s,--stop stop pmie processes instead of starting them + -T,--terse produce a terser form of output + -V,--verbose increase diagnostic verbosity + --help +EOF + +ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"` +[ $? != 0 ] && exit 1 + +eval set -- "$ARGS" +while [ $# -gt 0 ] +do + case "$1" + in + -c) CONTROL="$2" + shift + ;; + -C) CHECK_RUNLEVEL=true + ;; + -N) SHOWME=true + MV="echo + mv" + RM="echo + rm" + CP="echo + cp" + KILL="echo + kill" + ;; + -s) START_PMIE=false + ;; + -T) TERSE=true + ;; + -V) if $VERBOSE + then + VERY_VERBOSE=true + else + VERBOSE=true + fi + ;; + --) shift + break + ;; + -\?) pmgetopt --usage --progname=$prog --config=$tmp/usage + status=1 + exit + ;; + esac + shift +done + +if [ $# -ne 0 ] +then + pmgetopt --usage --progname=$prog --config=$tmp/usage + status=1 + exit +fi + +_error() +{ + echo "$prog: [$CONTROL:$line]" + echo "Error: $1" + echo "... automated performance reasoning for host \"$host\" unchanged" + touch $tmp/err +} + +_warning() +{ + echo "$prog [$CONTROL:$line]" + echo "Warning: $1" +} + +_message() +{ + case $1 + in + 'restart') + $PCP_ECHO_PROG $PCP_ECHO_N "Restarting pmie for host \"$host\" ...""$PCP_ECHO_C" + ;; + esac +} + +_lock() +{ + # demand mutual exclusion + # + rm -f $tmp/stamp + delay=200 # tenths of a second + while [ $delay -ne 0 ] + do + if pmlock -v $logfile.lock >$tmp/out + then + echo $logfile.lock >$tmp/lock + break + else + if [ ! -f $tmp/stamp ] + then + touch -t `pmdate -30M %Y%m%d%H%M` $tmp/stamp + fi + if [ -n "`find $logfile.lock ! -newer $tmp/stamp -print 2>/dev/null`" ] + then + _warning "removing lock file older than 30 minutes" + ls -l $logfile.lock + rm -f $logfile.lock + fi + fi + pmsleep 0.1 + delay=`expr $delay - 1` + done + + if [ $delay -eq 0 ] + then + # failed to gain mutex lock + # + if [ -f $logfile.lock ] + then + _warning "is another PCP cron job running concurrently?" + ls -l $logfile.lock + else + echo "$prog: `cat $tmp/out`" + fi + _warning "failed to acquire exclusive lock ($logfile.lock) ..." + continue + fi +} + +_unlock() +{ + rm -f $logfile.lock + echo >$tmp/lock +} + +_check_logfile() +{ + if [ ! -f $logfile ] + then + echo "$prog: Error: cannot find pmie output file at \"$logfile\"" + if $TERSE + then + : + else + logdir=`dirname $logfile` + echo "Directory (`cd $logdir; $PWDCMND`) contents:" + LC_TIME=POSIX ls -la $logdir + fi + else + echo "Contents of pmie output file \"$logfile\" ..." + cat $logfile + fi +} + +_check_pmie() +{ + $VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N " [process $1] ""$PCP_ECHO_C" + + # wait until pmie process starts, or exits + # + delay=5 + [ ! -z "$PMCD_CONNECT_TIMEOUT" ] && delay=$PMCD_CONNECT_TIMEOUT + x=5 + [ ! -z "$PMCD_REQUEST_TIMEOUT" ] && x=$PMCD_REQUEST_TIMEOUT + + # wait for maximum time of a connection and 20 requests + # + delay=`expr \( $delay + 20 \* $x \) \* 10` # tenths of a second + while [ $delay -ne 0 ] + do + if [ -f $logfile ] + then + # $logfile was previously removed, if it has appeared again then + # we know pmie has started ... if not just sleep and try again + # + if ls "$PCP_TMP_DIR/pmie/$1" >$tmp/out 2>&1 + then + if grep "No such file or directory" $tmp/out >/dev/null + then + : + else + $VERBOSE && echo " done" + return 0 + fi + fi + + _plist=`_get_pids_by_name pmie` + _found=false + for _p in `echo $_plist` + do + [ $_p -eq $1 ] && _found=true + done + + if $_found + then + # process still here, just hasn't created its status file + # yet, try again + : + else + $VERBOSE || _message restart + echo " process exited!" + if $TERSE + then + : + else + echo "$prog: Error: failed to restart pmie" + echo "Current pmie processes:" + $PCP_PS_PROG $PCP_PS_ALL_FLAGS | tee $tmp/tmp | sed -n -e 1p + for _p in `echo $_plist` + do + sed -n -e "/^[ ]*[^ ]* [ ]*$_p /p" < $tmp/tmp + done + echo + fi + _check_logfile + return 1 + fi + fi + pmsleep 0.1 + delay=`expr $delay - 1` + $VERBOSE && [ `expr $delay % 10` -eq 0 ] && \ + $PCP_ECHO_PROG $PCP_ECHO_N ".""$PCP_ECHO_C" + done + $VERBOSE || _message restart + echo " timed out waiting!" + if $TERSE + then + : + else + sed -e 's/^/ /' $tmp/out + fi + _check_logfile + return 1 +} + +_get_configfile() +{ + # extract the pmie configuration file (-c) from a list of arguments + # + echo $@ | sed -n \ + -e 's/^/ /' \ + -e 's/[ ][ ]*/ /g' \ + -e 's/-c /-c/' \ + -e 's/.* -c\([^ ]*\).*/\1/p' +} + +_configure_pmie() +{ + # update a pmie configuration file if it should be created/modified + # + configfile="$1" + + if [ -f "$configfile" ] + then + # look for "magic" string at start of file, and ensure we created it + sed 1q "$configfile" | grep '^// pmieconf-pmie [0-9]' >/dev/null + magic=$? + grep '^// Auto-generated by pmieconf' "$configfile" >/dev/null + owned=$? + if [ $magic -eq 0 -a $owned -eq 0 ] + then + # pmieconf file, see if re-generation is needed + cp "$configfile" $tmp/pmie + if $PMIECONF -f $tmp/pmie $CONFARGS >$tmp/diag 2>&1 + then + grep -v "generated by pmieconf" "$configfile" >$tmp/old + grep -v "generated by pmieconf" $tmp/pmie >$tmp/new + if ! diff $tmp/old $tmp/new >/dev/null + then + if [ -w $configfile ] + then + $VERBOSE && echo "Reconfigured: \"$configfile\" (pmieconf)" + eval $CP $tmp/pmie "$configfile" + else + _warning "no write access to pmieconf file \"$configfile\", skip reconfiguration" + ls -l "$configfile" + fi + fi + else + _warning "pmieconf failed to reconfigure \"$configfile\"" + cat "s;$tmp/pmie;$configfile;g" $tmp/diag + echo "=== start pmieconf file ===" + cat $tmp/pmie + echo "=== end pmieconf file ===" + fi + fi + elif [ ! -e "$configfile" ] + then + # file does not exist, generate it, if possible + if $SHOWME + then + echo "+ $PMIECONF -f $configfile $CONFARGS" + elif ! $PMIECONF -f "$configfile" $CONFARGS >$tmp/diag 2>&1 + then + _warning "pmieconf failed to generate \"$configfile\"" + cat $tmp/diag + echo "=== start pmieconf file ===" + cat "$configfile" + echo "=== end pmieconf file ===" + else + chown $PCP_USER:$PCP_GROUP "$configfile" >/dev/null 2>&1 + fi + fi +} + +QUIETLY=false +if [ $CHECK_RUNLEVEL = true ] +then + # determine whether to start/stop based on runlevel settings - we + # need to do this when running unilaterally from cron, else we'll + # always start pmie up (even when we shouldn't). + # + QUIETLY=true + if is_chkconfig_on pmie + then + START_PMIE=true + else + START_PMIE=false + fi +fi + +if [ $START_PMIE = false ] +then + # if pmie has never been started, there's no work to do to stop it + [ ! -d "$PCP_TMP_DIR/pmie" ] && exit + $QUIETLY || $PCP_BINADM_DIR/pmpost "stop pmie from $prog" +fi + +if [ ! -f "$CONTROL" ] +then + echo "$prog: Error: cannot find control file ($CONTROL)" + status=1 + exit +fi + +# 1.0 is the first release, and the version is set in the control file +# with a $version=x.y line +# +version=1.0 +eval `grep '^version=' "$CONTROL" | sort -rn` +if [ $version != "1.0" ] +then + _error "unsupported version (got $version, expected 1.0)" + status=1 + exit +fi + +echo >$tmp/dir +rm -f $tmp/err $tmp/pmies + +line=0 +cat "$CONTROL" \ + | sed -e "s;PCP_LOG_DIR;$PCP_LOG_DIR;g" \ + | while read host socks logfile args +do + # start in one place for each iteration (beware relative paths) + cd "$here" + line=`expr $line + 1` + + # NB: FQDN cleanup: substitute the LOCALHOSTNAME marker in the config line + # differently for the directory and the pcp -h HOST arguments. + logfile_hostname=`hostname || echo localhost` + logfile=`echo $logfile | sed -e "s;LOCALHOSTNAME;$logfile_hostname;"` + logfile=`_unsymlink_path $logfile` + [ "x$host" = "xLOCALHOSTNAME" ] && host=local: + + case "$host" + in + \#*|'') # comment or empty + continue + ;; + \$*) # in-line variable assignment + $SHOWME && echo "# $host $socks $logfile $args" + cmd=`echo "$host $socks $logfile $args" \ + | sed -n \ + -e "/='/s/\(='[^']*'\).*/\1/" \ + -e '/="/s/\(="[^"]*"\).*/\1/' \ + -e '/=[^"'"'"']/s/[;&<>|].*$//' \ + -e '/^\\$[A-Za-z][A-Za-z0-9_]*=/{ +s/^\\$// +s/^\([A-Za-z][A-Za-z0-9_]*\)=/export \1; \1=/p +}'` + if [ -z "$cmd" ] + then + # in-line command, not a variable assignment + _warning "in-line command is not a variable assignment, line ignored" + else + case "$cmd" + in + 'export PATH;'*) + _warning "cannot change \$PATH, line ignored" + ;; + 'export IFS;'*) + _warning "cannot change \$IFS, line ignored" + ;; + *) + $SHOWME && echo "+ $cmd" + eval $cmd + ;; + esac + fi + continue + ;; + esac + + if [ -z "$socks" -o -z "$logfile" -o -z "$args" ] + then + _error "insufficient fields in control file record" + continue + fi + + $VERY_VERBOSE && echo "Check pmie -h $host -l $logfile ..." + + # make sure output directory exists + # + dir=`dirname $logfile` + if [ ! -d "$dir" ] + then + mkdir -p -m 755 "$dir" >$tmp/err 2>&1 + if [ ! -d "$dir" ] + then + cat $tmp/err + _error "cannot create directory ($dir) for pmie log file" + continue + fi + chown $PCP_USER:$PCP_GROUP "$dir" >/dev/null 2>&1 + fi + + cd "$dir" + dir=`$PWDCMND` + $SHOWME && echo "+ cd $dir" + + # ensure pcp user will be able to write there + # + chown -R $PCP_USER:$PCP_GROUP "$dir" >/dev/null 2>&1 + if [ ! -w "$dir" ] + then + _warning "no write access in $dir, skip lock file processing" + ls -ld "$dir" + else + _lock + fi + + # match $logfile from control file to running pmies + pid="" + for file in $PCP_TMP_DIR/pmie/[0-9]* + do + [ "$file" = "$PCP_TMP_DIR/pmie/[0-9]*" ] && continue + $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "... try $file: ""$PCP_ECHO_C" + + p_id=`echo $file | sed -e 's,.*/,,'` + p_logfile="" + p_pmcd_host="" + + # throw away stderr in case $file has been removed by now + eval `$PCP_BINADM_DIR/pmiestatus $file 2>/dev/null | $PCP_AWK_PROG ' +NR == 2 { printf "p_logfile=\"%s\"\n", $0; next } +NR == 3 { printf "p_pmcd_host=\"%s\"\n", $0; next } + { next }'` + + p_logfile=`_unsymlink_path $p_logfile` + if [ "$p_logfile" != $logfile ] + then + $VERY_VERBOSE && echo "different logfile, skip" + $VERY_VERBOSE && echo " $p_logfile differs to $logfile" + elif _get_pids_by_name pmie | grep "^$p_id\$" >/dev/null + then + $VERY_VERBOSE && echo "pmie process $p_id identified, OK" + pid=$p_id + break + else + $VERY_VERBOSE && echo "pmie process $p_id not running, skip" + $VERY_VERBOSE && _get_pids_by_name pmie + fi + done + + if $VERY_VERBOSE + then + if [ -z "$pid" ] + then + echo "No current pmie process exists for:" + else + echo "Found pmie process $pid monitoring:" + fi + echo " host = $host" + echo " log file = $logfile" + fi + + if [ -z "$pid" -a $START_PMIE = true ] + then + configfile=`_get_configfile $args` + if [ ! -z "$configfile" ] + then + # if this is a relative path and not relative to cwd, + # substitute in the default pmie search location. + # + if [ ! -f "$configfile" -a "`basename $configfile`" = "$configfile" ] + then + configfile="$PCP_SYSCONF_DIR/pmie/$configfile" + fi + + # check configuration file exists and is up to date + _configure_pmie "$configfile" "$host" + fi + + args="-h $host -l $logfile $args" + + $VERBOSE && _message restart + + sock_me='' + if [ "$socks" = y ] + then + # only check for pmsocks if it's specified in the control file + have_pmsocks=false + if which pmsocks >/dev/null 2>&1 + then + # check if pmsocks has been set up correctly + if pmsocks ls >/dev/null 2>&1 + then + have_pmsocks=true + fi + fi + + if $have_pmsocks + then + sock_me="pmsocks " + else + echo "$prog: Warning: no pmsocks available, would run without" + sock_me="" + fi + fi + + [ -f "$logfile" ] && eval $MV -f "$logfile" "$logfile.prior" + + if $SHOWME + then + $VERBOSE && echo + echo "+ ${sock_me}$PMIE -b $args" + _unlock + continue + else + # since this is launched as a sort of daemon, any output should + # go on pmie's stderr, i.e. $logfile ... use -b for this + # + $VERY_VERBOSE && ( echo; $PCP_ECHO_PROG $PCP_ECHO_N "+ ${sock_me}$PMIE -b $args""$PCP_ECHO_C"; echo "..." ) + $PCP_BINADM_DIR/pmpost "start pmie from $prog for host $host" + ${sock_me}$PMIE -b $args & + pid=$! + fi + + # wait for pmie to get started, and check on its health + _check_pmie $pid + + elif [ ! -z "$pid" -a $START_PMIE = false ] + then + # Send pmie a SIGTERM, which is noted as a pending shutdown. + # Add pid to list of pmies sent SIGTERM - may need SIGKILL later. + # + $VERY_VERBOSE && echo "+ $KILL -s TERM $pid" + eval $KILL -s TERM $pid + $PCP_ECHO_PROG $PCP_ECHO_N "$pid ""$PCP_ECHO_C" >> $tmp/pmies + fi + + _unlock +done + +# check all the SIGTERM'd pmies really died - if not, use a bigger hammer. +# +if $SHOWME +then + : +elif [ $START_PMIE = false -a -s $tmp/pmies ] +then + pmielist=`cat $tmp/pmies` + if ps -p "$pmielist" >/dev/null 2>&1 + then + $VERY_VERBOSE && ( echo; $PCP_ECHO_PROG $PCP_ECHO_N "+ $KILL -KILL `cat $tmp/pmies` ...""$PCP_ECHO_C" ) + eval $KILL -s KILL $pmielist >/dev/null 2>&1 + delay=30 # tenths of a second + while ps -f -p "$pmielist" >$tmp/alive 2>&1 + do + if [ $delay -gt 0 ] + then + pmsleep 0.1 + delay=`expr $delay - 1` + continue + fi + echo "$prog: Error: pmie process(es) will not die" + cat $tmp/alive + status=1 + break + done + fi +fi + +[ -f $tmp/err ] && status=1 +exit diff --git a/src/pmie/pmie_daily.sh b/src/pmie/pmie_daily.sh new file mode 100644 index 0000000..3e386c0 --- /dev/null +++ b/src/pmie/pmie_daily.sh @@ -0,0 +1,540 @@ +#! /bin/sh +# +# Copyright (c) 2013-2014 Red Hat. +# Copyright (c) 2007 Aconex. All Rights Reserved. +# Copyright (c) 1995-2000,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. +# +# Daily administrative script for pmie logfiles +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/rc-proc.sh + +# added to handle problem when /var/log/pcp is a symlink, as first +# reported by Micah_Altman@harvard.edu in Nov 2001 +# +_unsymlink_path() +{ + [ -z "$1" ] && return + __d=`dirname $1` + __real_d=`cd $__d 2>/dev/null && $PWDCMND` + if [ -z "$__real_d" ] + then + echo $1 + else + echo $__real_d/`basename $1` + fi +} + +# error messages should go to stderr, not the GUI notifiers +# +unset PCP_STDERR + +# constant setup +# +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +status=0 +echo >$tmp/lock +trap "rm -rf \`[ -f $tmp/lock ] && cat $tmp/lock\` $tmp; exit \$status" 0 1 2 3 15 +prog=`basename $0` + +if is_chkconfig_on pmie +then + PMIE_CTL=on +else + PMIE_CTL=off +fi + +# control file for pmie administration ... edit the entries in this +# file to reflect your local configuration (see also -c option below) +# +CONTROL=$PCP_PMIECONTROL_PATH + +# default number of days to keep pmie logfiles +# +CULLAFTER=14 + +# default compression program and days until starting compression +# +COMPRESS=xz +COMPRESSAFTER="" +COMPRESSREGEX="\.(meta|index|Z|gz|bz2|zip|xz|lzma|lzo|lz4)$" + +# mail addresses to send daily logfile summary to +# +MAILME="" + +# search for your mail agent of choice ... +# +MAIL='' +for try in Mail mail email +do + if which $try >/dev/null 2>&1 + then + MAIL=$try + break + fi +done + +# NB: FQDN cleanup; don't guess a 'real name for localhost', and +# definitely don't truncate it a la `hostname -s`. Instead now +# we use such a string only for the default log subdirectory, ie. +# for substituting LOCALHOSTNAME in the third column of $CONTROL. + +# determine path for pwd command to override shell built-in +# (see BugWorks ID #595416). +PWDCMND=`which pwd 2>/dev/null | $PCP_AWK_PROG ' +BEGIN { i = 0 } +/ not in / { i = 1 } +/ aliased to / { i = 1 } + { if ( i == 0 ) print } +'` +if [ -z "$PWDCMND" ] +then + # Looks like we have no choice here... + # force it to a known IRIX location + PWDCMND=/bin/pwd +fi +eval $PWDCMND -P >/dev/null 2>&1 +[ $? -eq 0 ] && PWDCMND="$PWDCMND -P" +here=`$PWDCMND` + +echo > $tmp/usage +cat >> $tmp/usage <<EOF +Options: + -c=FILE,--control=FILE pmie control file + -k=N,--discard=N remove pmie log files after N days + -m=ADDRs,--mail=ADDRs send daily log files to email addresses + -N,--showme perform a dry run, showing what would be done + -V,--verbose verbose output (multiple times for very verbose) + -x=N,--compress-after=N compress pmie log files after N days + -X=PROGRAM,--compressor=PROGRAM use PROGRAM for pmie log file compression + -Y=REGEX,--regex=REGEX egrep filter when compressing files ["$COMPRESSREGEX"] + --help +EOF + +_usage() +{ + pmgetopt --progname=$prog --config=$tmp/usage --usage + status=1 + exit +} + +# option parsing +# +SHOWME=false +RM=rm +KILL=pmsignal +VERBOSE=false +VERY_VERBOSE=false +MYARGS="" + +ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"` +[ $? != 0 ] && exit 1 + +eval set -- "$ARGS" +while [ $# -gt 0 ] +do + case "$1" + in + -c) CONTROL="$2" + shift + ;; + -k) CULLAFTER="$2" + shift + check=`echo "$CULLAFTER" | sed -e 's/[0-9]//g'` + if [ ! -z "$check" -a X"$check" != Xforever ] + then + echo "Error: -k option ($CULLAFTER) must be numeric" + status=1 + exit + fi + ;; + -m) MAILME="$2" + shift + ;; + -N) SHOWME=true + RM="echo + rm" + KILL="echo + kill" + MYARGS="$MYARGS -N" + ;; + -V) if $VERBOSE + then + VERY_VERBOSE=true + else + VERBOSE=true + fi + MYARGS="$MYARGS -V" + ;; + -x) COMPRESSAFTER="$2" + shift + check=`echo "$COMPRESSAFTER" | sed -e 's/[0-9]//g'` + if [ ! -z "$check" ] + then + echo "Error: -x option ($COMPRESSAFTER) must be numeric" + status=1 + exit + fi + ;; + -X) COMPRESS="$2" + shift + ;; + -Y) COMPRESSREGEX="$2" + shift + ;; + --) shift + break + ;; + -\?) _usage + ;; + esac + shift +done + +[ $# -ne 0 ] && _usage + +if [ ! -f "$CONTROL" ] +then + echo "$prog: Error: cannot find control file ($CONTROL)" + status=1 + exit +fi + +SUMMARY_LOGNAME=`pmdate -1d %Y%m%d` + +_error() +{ + _report Error "$1" +} + +_warning() +{ + _report Warning "$1" +} + +_report() +{ + echo "$prog: $1: $2" + echo "[$CONTROL:$line] ... inference engine for host \"$host\" unchanged" + touch $tmp/err +} + +_unlock() +{ + rm -f lock + echo >$tmp/lock +} + +# filter for pmie log files in working directory - +# pass in the number of days to skip over (backwards) from today +# +# pv:821339 too many sed commands for IRIX ... split into groups +# of at most 200 days +# +_date_filter() +{ + # start with all files whose names match the patterns used by + # the PCP pmie log file management scripts ... this list may be + # reduced by the sed filtering later on + # + ls | sed -n >$tmp/in -e '/[-.][12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]/p' + + i=0 + while [ $i -le $1 ] + do + dmax=`expr $i + 200` + [ $dmax -gt $1 ] && dmax=$1 + echo "/[-.][12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]/{" >$tmp/sed1 + while [ $i -le $dmax ] + do + x=`pmdate -${i}d %Y%m%d` + echo "/[-.]$x/d" >>$tmp/sed1 + i=`expr $i + 1` + done + echo "p" >>$tmp/sed1 + echo "}" >>$tmp/sed1 + + # cull file names with matching dates, keep other file names + # + sed -n -f $tmp/sed1 <$tmp/in >$tmp/tmp + mv $tmp/tmp $tmp/in + done + + cat $tmp/in +} + + +rm -f $tmp/err $tmp/mail +line=0 +version='' +cat $CONTROL \ +| sed -e "s;PCP_LOG_DIR;$PCP_LOG_DIR;g" \ +| while read host socks logfile args +do + # start in one place for each iteration (beware relative paths) + cd "$here" + line=`expr $line + 1` + + # NB: FQDN cleanup: substitute the LOCALHOSTNAME marker in the config line + # differently for the directory and the pcp -h HOST arguments. + logfile_hostname=`hostname || echo localhost` + logfile=`echo $logfile | sed -e "s;LOCALHOSTNAME;$logfile_hostname;"` + logfile=`_unsymlink_path $logfile` + [ "x$host" = "xLOCALHOSTNAME" ] && host=local: + + $VERY_VERBOSE && echo "[control:$line] host=\"$host\" socks=\"$socks\" log=\"$logfile\" args=\"$args\"" + + case "$host" + in + \#*|'') # comment or empty + continue + ;; + + \$*) # in-line variable assignment + $SHOWME && echo "# $host $socks $logfile $args" + cmd=`echo "$host $socks $logfile $args" \ + | sed -n \ + -e "/='/s/\(='[^']*'\).*/\1/" \ + -e '/="/s/\(="[^"]*"\).*/\1/' \ + -e '/=[^"'"'"']/s/[;&<>|].*$//' \ + -e '/^\\$[A-Za-z][A-Za-z0-9_]*=/{ +s/^\\$// +s/^\([A-Za-z][A-Za-z0-9_]*\)=/export \1; \1=/p +}'` + if [ -z "$cmd" ] + then + # in-line command, not a variable assignment + _warning "in-line command is not a variable assignment, line ignored" + else + case "$cmd" + in + 'export PATH;'*) + _warning "cannot change \$PATH, line ignored" + ;; + 'export IFS;'*) + _warning "cannot change \$IFS, line ignored" + ;; + *) + $SHOWME && echo "+ $cmd" + eval $cmd + ;; + esac + fi + continue + ;; + esac + + if [ -z "$socks" -o -z "$logfile" -o -z "$args" ] + then + _error "insufficient fields in control file record" + continue + fi + + dir=`dirname $logfile` + $VERY_VERBOSE && echo "Check pmie -h $host ... in $dir ..." + + if [ ! -d "$dir" ] + then + if [ "$PMIE_CTL" = "on" ] + then + _error "logfile directory ($dir) does not exist" + fi + continue + fi + + cd $dir + dir=`$PWDCMND` + $SHOWME && echo "+ cd $dir" + + if $VERBOSE + then + echo + echo "=== daily maintenance of pmie log files for host $host ===" + echo + fi + + if [ ! -w $dir ] + then + echo "$prog: Warning: no write access in $dir, skip lock file processing" + else + # demand mutual exclusion + # + fail=true + rm -f $tmp/stamp + for try in 1 2 3 4 + do + if pmlock -v lock >$tmp/out + then + echo $dir/lock >$tmp/lock + fail=false + break + else + if [ ! -f $tmp/stamp ] + then + touch -t `pmdate -30M %Y%m%d%H%M` $tmp/stamp + fi + if [ ! -z "`find lock -newer $tmp/stamp -print 2>/dev/null`" ] + then + : + else + echo "$prog: Warning: removing lock file older than 30 minutes" + LC_TIME=POSIX ls -l $dir/lock + rm -f lock + fi + fi + sleep 5 + done + + if $fail + then + # failed to gain mutex lock + # + if [ -f lock ] + then + echo "$prog: Warning: is another PCP cron job running concurrently?" + LC_TIME=POSIX ls -l $dir/lock + else + echo "$prog: `cat $tmp/out`" + fi + _warning "failed to acquire exclusive lock ($dir/lock) ..." + continue + fi + fi + + # match $logfile from control file to running pmies + pid="" + $VERY_VERBOSE && echo "Looking for logfile=$logfile" + for file in `ls "$PCP_TMP_DIR/pmie"` + do + p_id=$file + file="$PCP_TMP_DIR/pmie/$file" + p_logfile="" + p_pmcd_host="" + + $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "Check p_id=$p_id ... ""$PCP_ECHO_C" + if ps -p "$p_id" >/dev/null 2>&1 + then + eval `$PCP_BINADM_DIR/pmiestatus $file | $PCP_AWK_PROG ' +NR == 2 { printf "p_logfile=\"%s\"\n", $0; next } +NR == 3 { printf "p_pmcd_host=\"%s\"\n", $0; next } + { next }'` + $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "p_pmcd_host=$p_pmcd_host p_logfile=$p_logfile""$PCP_ECHO_C" + p_logfile=`_unsymlink_path $p_logfile` + $VERY_VERBOSE && $PCP_ECHO_PROG $PCP_ECHO_N "->$p_logfile ... ""$PCP_ECHO_C" + if [ "$p_logfile" = $logfile ] + then + pid=$p_id + $VERY_VERBOSE && $PCP_ECHO_PROG match + break + fi + $VERY_VERBOSE && $PCP_ECHO_PROG "no match" + else + # ignore, its not a running process + eval $RM -f $file + $VERY_VERBOSE && $PCP_ECHO_PROG "process has vanished" + fi + done + + if [ -z "$pid" ] + then + if [ "$PMIE_CTL" = "on" ] + then + _error "no pmie instance running for host \"$host\"" + fi + else + # now move current logfile name aside and SIGHUP to "roll the logs" + # creating a new logfile with the old name in the process. + # + $SHOWME && echo "+ mv $logfile ${logfile}.{SUMMARY_LOGNAME}" + if mv $logfile ${logfile}.${SUMMARY_LOGNAME} + then + $VERY_VERBOSE && echo "+ $KILL -s HUP $pid" + eval $KILL -s HUP $pid + echo ${logfile}.${SUMMARY_LOGNAME} >> $tmp/mail + else + _error "problems moving logfile \"$logfile\" for host \"$host\"" + touch $tmp/err + fi + fi + + # and cull old logfiles + # + if [ X"$CULLAFTER" != X"forever" ] + then + _date_filter $CULLAFTER >$tmp/list + if [ -s $tmp/list ] + then + if $VERBOSE + then + echo "Log files older than $CULLAFTER days being removed ..." + fmt <$tmp/list | sed -e 's/^/ /' + fi + if $SHOWME + then + cat $tmp/list | xargs echo + rm -f + else + cat $tmp/list | xargs rm -f + fi + fi + fi + + # finally, compress old log files + # (after cull - don't compress unnecessarily) + # + if [ ! -z "$COMPRESSAFTER" ] + then + _date_filter $COMPRESSAFTER | egrep -v "$COMPRESSREGEX" >$tmp/list + if [ -s $tmp/list ] + then + if $VERBOSE + then + echo "Log files older than $COMPRESSAFTER days being compressed ..." + fmt <$tmp/list | sed -e 's/^/ /' + fi + if $SHOWME + then + cat $tmp/list | xargs echo + $COMPRESS + else + cat $tmp/list | xargs $COMPRESS + fi + fi + fi + + _unlock + +done + +if [ -n "$MAILME" -a -s $tmp/mail ] +then + logs="" + for file in `cat $tmp/mail` + do + [ -f $file ] && logs="$logs $file" + done + egrep -v '( OK | OK$|^$|^Log |^pmie: PID)' $logs > $tmp/logmail + if [ ! -s "$tmp/logmail" ] + then + : + elif [ ! -z "$MAIL" ] + then + egrep -v '( OK | OK$|^$)' $logs | \ + $MAIL -s "PMIE summary for $LOCALHOSTNAME" $MAILME + else + echo "$prog: PMIE summary for $LOCALHOSTNAME ..." + egrep -v '( OK | OK$|^$)' $logs + fi + rm -f $tmp/mail $tmp/logmail +fi + +[ -f $tmp/err ] && status=1 +exit diff --git a/src/pmie/rc_pmie b/src/pmie/rc_pmie new file mode 100644 index 0000000..acef291 --- /dev/null +++ b/src/pmie/rc_pmie @@ -0,0 +1,273 @@ +#!/bin/sh +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2000 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. +# +# The following is for chkconfig on RedHat based systems +# chkconfig: 2345 94 06 +# description: pmie is a performance inference engine for the Performance Co-Pilot (PCP) +# +# The following is for insserv(1) based systems, +# e.g. SuSE, where chkconfig is a perl script. +### BEGIN INIT INFO +# Provides: pmie +# Required-Start: $remote_fs +# Should-Start: $local_fs $network $syslog $time $pmcd +# Required-Stop: $remote_fs +# Should-Stop: $local_fs $network $syslog $pmcd +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Control pmie (performance inference engine for PCP) +# Description: Configure and control pmie (the performance inference engine for the Performance Co-Pilot) +### END INIT INFO + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/rc-proc.sh + +PMIECTRL=$PCP_PMIECONTROL_PATH +pmprog=$PCP_RC_DIR/pmie +prog=$PCP_RC_DIR/`basename $0` + +# search for your mail agent of choice ... +# +MAIL='' +for try in Mail mail email +do + if which $try >/dev/null 2>&1 + then + MAIL=$try + break + fi +done + +tmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1 +status=1 +trap "rm -rf $tmp; exit \$status" 0 1 2 3 15 + +if [ $pmprog = $prog ] +then + VERBOSE_CTL=on +else + VERBOSE_CTL=off +fi + +id=`id | sed -e "s/(.*//" -e "s/.*=//"` +if [ "$id" != 0 -a "$1" != "status" ] +then + if [ -n "$PCP_DIR" ] + then + : running in a non-default installation, do not need to be root + else + echo "$prog:"' +Error: You must be root (uid 0) to start or stop pmie.' + exit + fi +fi + +_usage() +{ + echo "Usage: $pmprog [-v] {start|restart|condrestart|stop|status|reload|force-reload}" +} + +_reboot_setup() +{ + # base directories and house-keeping for pmie instances + # + if [ ! -d "$PCP_TMP_DIR/pmie" ] + then + mkdir -p -m 0775 "$PCP_TMP_DIR/pmie" + chown $PCP_USER:$PCP_GROUP "$PCP_TMP_DIR/pmie" + else + rm -rf $tmp/ent $tmp/pid + here=`pwd` + cd "$PCP_TMP_DIR/pmie" + _get_pids_by_name pmie | sort >$tmp/pid + ls [0-9]* 2>&1 | sed -e '/\[0-9]\*/d' \ + | sed -e 's/[ ][ ]*//g' | sort >$tmp/ent + # remove entries without a pmie process + rm -f `comm -23 $tmp/ent $tmp/pid` + rm -f $tmp/ent $tmp/pid + cd "$here" + fi +} + +# Note: _start_pmie is running in the background, in parallel with +# the rest of the script. It might complete well after the caller +# so tmpfile handling is especially problematic. Goal is to speed +# bootup by starting potentially slow (remote monitoring) processes +# in the background. +# +_start_pmie() +{ + bgstatus=0 + bgtmp=`mktemp -d /var/tmp/pcp.XXXXXXXXX` || exit 1 + trap "rm -rf $bgtmp; exit \$bgstatus" 0 1 2 3 15 + + wait_option='' + [ ! -z "$PMCD_WAIT_TIMEOUT" ] && wait_option="-t $PMCD_WAIT_TIMEOUT" + + if pmcd_wait $wait_option + then + pmie_check >$bgtmp/pmie 2>&1 + bgstatus=$? + if [ -s $bgtmp/pmie ] + then + $PCP_BINADM_DIR/pmpost "pmie_check start failed in $prog, mailing output to root" + if [ ! -z "$MAIL" ] + then + $MAIL -s "pmie_check start failed in $prog" root <$bgtmp/pmie >/dev/null 2>&1 + else + echo "$prog: pmie_check start failed ..." + cat $bgtmp/pmie + fi + fi + else + bgstatus=$? + $PCP_BINADM_DIR/pmpost "pmcd_wait failed in $prog: exit status: $bgstatus" + if [ ! -z "$MAIL" ] + then + echo "pmcd_wait: exit status: $bgstatus" | $MAIL -s "pmcd_wait failed in $prog" root + else + echo "$prog: pmcd_wait failed ..." + echo "pmcd_wait: exit status: $bgstatus" + fi + fi + exit $bgstatus # co-process is now complete +} + +_shutdown() +{ + _get_pids_by_name pmie >$tmp/pmies 2>&1 + if [ ! -s $tmp/pmies ] + then + [ "$1" = verbose ] && echo "$pmprog: PMIE not running" + return 0 + fi + + [ "$1" = quietly ] || \ + $ECHO $PCP_ECHO_N "Waiting for pmie process(es) to terminate ..." "$PCP_ECHO_C" + + pmie_check -s >$tmp/pmie 2>&1 + if [ -s $tmp/pmie ] + then + $PCP_BINADM_DIR/pmpost "pmie_check stop failed in $prog, mailing output to root" + if [ ! -z "$MAIL" ] + then + $MAIL -s "pmie_check stop failed in $prog" root <$tmp/pmie + else + echo "$prog: pmie_check stop failed ..." + cat $tmp/pmie + fi + fi + + true + $RC_STATUS -v + rm -fr "$tmp/pmie" "$PCP_TMP_DIR"/pmie/* + $PCP_BINADM_DIR/pmpost "stop pmie from $pmprog" +} + +while getopts v c +do + case $c + in + v) # force verbose + VERBOSE_CTL=on + ;; + *) + _usage + exit 1 + ;; + esac +done +shift `expr $OPTIND - 1` + +if [ $VERBOSE_CTL = on ] +then # For a verbose startup and shutdown + ECHO=$PCP_ECHO_PROG +else # For a quiet startup and shutdown + ECHO=: +fi + +case "$1" in + 'start'|'restart'|'condrestart'|'reload'|'force-reload') + if [ "$1" = "condrestart" ] && ! is_chkconfig_on pmie + then + status=0 + exit + fi + + _shutdown quietly + + # messages should go to stderr, not the GUI notifiers + # + unset PCP_STDERR + + _reboot_setup + + if which pmie >/dev/null 2>&1 + then + if is_chkconfig_on pmie + then + if which pmie_check >/dev/null 2>&1 + then + if [ ! -f $PMIECTRL ] + then + echo "$prog:"' +Error: PCP inference engine control file '$PMIECTRL' + is missing! Cannot start any Performance Co-Pilot inference engine(s).' + else + $ECHO $PCP_ECHO_N "Starting pmie ..." "$PCP_ECHO_C" + _start_pmie & + $RC_STATUS -v + fi + fi + elif [ "$0" = "$PCP_RC_DIR/pmie" ] + then + echo "$prog: Warning: Performance Co-Pilot Inference Engine (pmie) is disabled." + chkconfig_on_msg pmie + fi + fi + status=0 + ;; + + 'stop') + _shutdown verbose + status=0 + ;; + + 'status') + # NOTE: $RC_CHECKPROC returns LSB compliant status values. + $ECHO $PCP_ECHO_N "Checking for pmie:" "$PCP_ECHO_C" + if [ -r /etc/rc.status ] + then + # SuSE + $RC_CHECKPROC pmie + $RC_STATUS -v + status=$? + else + # not SuSE + $RC_CHECKPROC pmie + status=$? + if [ $status -eq 0 ] + then + $ECHO running + else + $ECHO stopped + fi + fi + ;; + *) + _usage + ;; +esac + diff --git a/src/pmie/src/GNUmakefile b/src/pmie/src/GNUmakefile new file mode 100644 index 0000000..86984bb --- /dev/null +++ b/src/pmie/src/GNUmakefile @@ -0,0 +1,79 @@ +# +# 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. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +TARGET = pmie$(EXECSUFFIX) + +CFILES = pmie.c symbol.c dstruct.c lexicon.c syntax.c pragmatics.c eval.c \ + show.c match_inst.c systemlog.c stomp.c andor.c + +HFILES = fun.h dstruct.h eval.h lexicon.h pragmatics.h stats.h \ + show.h symbol.h syntax.h systemlog.h stomp.h andor.h + +SKELETAL = hdr.sk fetch.sk misc.sk aggregate.sk unary.sk binary.sk \ + merge.sk act.sk + +LSRCFILES = $(SKELETAL) meta logger.h + +LDIRT += $(YFILES:%.y=%.tab.?) fun.c fun.o $(TARGET) grammar.h + +LLDLIBS = $(PCPLIB) $(LIB_FOR_MATH) $(LIB_FOR_REGEX) + +LCFLAGS += $(PIECFLAGS) +LLDFLAGS += $(PIELDFLAGS) + +default: $(TARGET) + +YFILES = grammar.y + +.NOTPARALLEL: +grammar.h grammar.tab.c: grammar.y + $(YACC) -d -b `basename $< .y` $< && cp `basename $@ .h`.tab.h $@ + +pmie$(EXECSUFFIX): $(OBJECTS) fun.o + $(CCF) -o $@ $(LDFLAGS) $(OBJECTS) fun.o $(LDLIBS) + +install: default + $(INSTALL) -m 755 $(TARGET) $(PCP_BIN_DIR)/$(TARGET) + +lexicon.o syntax.o: grammar.h + +fun.o: fun.h + +fun.c: $(SKELETAL) meta + @echo $@ + ./meta + +include $(BUILDRULES) + +default_pcp: default + +install_pcp: install + +fun.h: andor.h +andor.o dstruct.o eval.o fun.o grammar.tab.o lexicon.o match_inst.o pmie.o pragmatics.o show.o syntax.o systemlog.o: dstruct.h +dstruct.o eval.o pmie.o pragmatics.o syntax.o systemlog.o: eval.h +andor.o dstruct.o eval.o fun.o match_inst.o: fun.h +lexicon.o syntax.o: grammar.h +grammar.tab.o lexicon.o show.o syntax.o: lexicon.h +systemlog.o: logger.h +andor.o dstruct.o eval.o fun.o grammar.tab.o lexicon.o pmie.o pragmatics.o show.o syntax.o: pragmatics.h +andor.o dstruct.o eval.o fun.o grammar.tab.o match_inst.o pmie.o show.o syntax.o: show.h +andor.o fun.o grammar.tab.o pmie.o stomp.o: stomp.h +andor.o dstruct.o eval.o fun.o grammar.tab.o lexicon.o match_inst.o pmie.o pragmatics.o show.o symbol.o syntax.o systemlog.o: symbol.h +grammar.tab.o lexicon.o pmie.o syntax.o systemlog.o: syntax.h +fun.o grammar.tab.o systemlog.o: systemlog.h + diff --git a/src/pmie/src/README.DEBUG b/src/pmie/src/README.DEBUG new file mode 100644 index 0000000..52eca13 --- /dev/null +++ b/src/pmie/src/README.DEBUG @@ -0,0 +1,19 @@ +Notes to assist in debugging pmie + +pmie command line debug flags + APPL0 - lexical scanning + APPL1 - parse/expression tree construction + APPL2 - expression execution + +macro EVALARG(x) expands to + if ((x)->op < NOP) ((x)->eval)(x); + +The source file fun.c is generated by expansion of all of the *.sk +"skeletal" files ... so changes need to be made in the *.sk files and +fun.c will be recreated in the make. + +The Expr structure (defined in dstruct.h) is at the core of the pmie +expression evaluation. + valid must be > 0 for there to be any values able to be + used in the expression evaluation + diff --git a/src/pmie/src/act.sk b/src/pmie/src/act.sk new file mode 100644 index 0000000..358ef53 --- /dev/null +++ b/src/pmie/src/act.sk @@ -0,0 +1,376 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * skeleton: act.sk - actions + * + ***********************************************************************/ + +/* + * operator: actAnd + */ +void +actAnd(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + + EVALARG(arg1) + EVALARG(arg2) + *(Boolean *)x->ring = (*(Boolean *)arg1->ring == B_TRUE) && (*(Boolean *)arg2->ring == B_TRUE); +} + + +/* + * operator: actOr + */ +void +actOr(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + + EVALARG(arg1) + if (*(Boolean *)arg1->ring == B_FALSE) { + EVALARG(arg2) + *(Boolean *)x->ring = *(Boolean *)arg2->ring; + } + else *(Boolean *)x->ring = B_TRUE; +} + + +/* + * operator: actShell + */ +void +actShell(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; +#ifndef IS_MINGW + pid_t pid; +#endif + int sts; + + if ((arg2 == NULL) || + (x->smpls[0].stamp == 0) || + (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp)) + { + EVALARG(arg1) + fflush(stdout); + fflush(stderr); +#ifdef IS_MINGW + putenv("IFS=\t\n"); + sts = system((char *)arg1->ring); + need_wait = 1; + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "spawn for shell failed\n"); + *(Boolean *)x->ring = B_FALSE; + } + else { + *(Boolean *)x->ring = B_TRUE; + x->smpls[0].stamp = now; + x->valid = 0; + } +#else /*POSIX*/ + pid = fork(); + if (pid == 0) { + /* child, run the command */ + setsid(); + putenv("PATH=/usr/sbin:/sbin:/usr/bin:/bin"); + putenv("IFS=\t\n"); + sts = system((char *)arg1->ring); + _exit(WEXITSTATUS(sts)); /* avoid atexit() handler */ + } + else if (pid > 0) { + /* parent, wait for child to exit to catch status */ +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "actShell: fork: pid=%" FMT_PID "\n", pid); + } +#endif + sts = waitpid(pid, &x->valid, 0); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "actShell: wait: pid=%" FMT_PID " status=0x%x", pid, x->valid); + if (WIFEXITED(x->valid)) + fprintf(stderr, " exit=%d", WEXITSTATUS(x->valid)); + if (WIFSIGNALED(x->valid)) + fprintf(stderr, " signal=%d", WTERMSIG(x->valid)); + fprintf(stderr, " (wait returns %d)\n", sts); + } +#endif + if (WIFEXITED(x->valid)) + x->valid = WEXITSTATUS(x->valid); + else + /* if no exit, then assume non-zero exit, hence failure! */ + x->valid = 1; + if (sts < 0 || x->valid != 0) + *(Boolean *)x->ring = B_FALSE; + else + *(Boolean *)x->ring = B_TRUE; + x->smpls[0].stamp = now; + } + else { + __pmNotifyErr(LOG_ERR, "fork for shell failed\n"); + *(Boolean *)x->ring = B_FALSE; + } +#endif + perf->actions++; + } +} + + +/* + * operator: actAlarm + */ +void +actAlarm(Expr *x) +{ + static char *alarmv[] = { + NULL, /* path to PCP_XCONFIRM_PROG inserted here */ + "-header", "Performance Co-Pilot Alarm", + "-b", "Cancel", + "-icon", "warning", + "-t", NULL, + "-t", NULL, + NULL}; + + char ctime[26]; + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + time_t clock; + int sts; + + if (alarmv[0] == NULL) { + /* + * one trip to get path for xconfirm(1) + */ + alarmv[0] = pmGetConfig("PCP_XCONFIRM_PROG"); + if (strcmp(alarmv[0], "") == 0) { + __pmNotifyErr(LOG_ERR, "PCP_XCONFIRM_PROG not found, using echo(1)\n"); + alarmv[0] = "/bin/echo"; + } + } + +#ifndef IS_MINGW + /* if old alarm still active, don't post new one */ + if (x->valid != 0) { + pid_t pid; + pid = waitpid((pid_t)x->valid, &sts, WNOHANG); + if (pid <= 0) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "actAlarm: wait: pid=%d not done (wait returns %" FMT_PID ")\n", x->valid, pid); + } +#endif + return; + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "actAlarm: wait: pid=%d done status=0x%x", x->valid, sts); + if (WIFEXITED(sts)) + fprintf(stderr, " exit=%d", WEXITSTATUS(sts)); + if (WIFSIGNALED(sts)) + fprintf(stderr, " signal=%d", WTERMSIG(sts)); + fprintf(stderr, " (wait returns %" FMT_PID ")\n", pid); + } +#endif + x->valid = 0; + } +#endif + + if ((arg2 == NULL) || + (x->smpls[0].stamp == 0) || + (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp)) + { + EVALARG(arg1) + clock = (time_t)(now+0.5); + pmCtime(&clock, ctime); +#ifdef IS_MINGW + alarmv[8] = ctime; + alarmv[10] = (char *)arg1->ring; + sts = spawnvp(_P_DETACH, alarmv[0], (const char * const *)alarmv); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "spawn for PCP_XCONFIRM_PROG failed\n"); + *(Boolean *)x->ring = B_FALSE; + } + else { + *(Boolean *)x->ring = B_TRUE; + x->smpls[0].stamp = now; + x->valid = 0; + } +#else + sts = fork(); + if (sts == 0) { + alarmv[8] = ctime; + alarmv[10] = (char *)arg1->ring; + setsid(); + if (strcmp(alarmv[0], "/bin/echo") != 0) { + /* only echo needs stdio, when xconfirm cannot be found */ + fclose(stdin); + fclose(stdout); + fclose(stderr); + } + execvp(alarmv[0], alarmv); + _exit(1); /* avoid atexit() handler */ + } + else if (sts > 0) { + need_wait = 1; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "actAlarm: fork: pid=%d\n", sts); + } +#endif + x->valid = sts; + *(Boolean *)x->ring = B_TRUE; + x->smpls[0].stamp = now; + } + else { + __pmNotifyErr(LOG_ERR, "fork for alarm failed\n"); + *(Boolean *)x->ring = B_FALSE; + } +#endif + perf->actions++; + } +} + + +/* + * operator: actSyslog + */ +void +actSyslog(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + int *pri; + char *tag; + + if ((arg2 == NULL) || + (x->smpls[0].stamp == 0) || + (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp)) + { + pri = (int *)arg1->arg2->ring; + tag = &((char *)arg1->arg2->ring)[sizeof(int)]; + EVALARG(arg1) + openlog(tag, LOG_PID|LOG_CONS, LOG_DAEMON); + if (arg1->ring == NULL) + syslog(*pri, "%s", ""); + else + syslog(*pri, "%s", (char *)arg1->ring); + closelog(); + *(Boolean *)x->ring = B_TRUE; + x->smpls[0].stamp = now; + perf->actions++; + } +} + + +/* + * operator: actPrint + */ +void +actPrint(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + time_t clock = (time_t)now; + char bfr[26]; + + if ((arg2 == NULL) || + (x->smpls[0].stamp == 0) || + (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp)) + { + EVALARG(arg1) + *(Boolean *)x->ring = B_TRUE; + x->smpls[0].stamp = now; + pmCtime(&clock, bfr); + bfr[24] = '\0'; + printf("%s: %s\n", bfr, (char *)arg1->ring); + fflush(stdout); + perf->actions++; + } +} + + +/* + * operator: actStomp + */ +void +actStomp(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + + if ((arg2 == NULL) || + (x->smpls[0].stamp == 0) || + (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp)) + { + EVALARG(arg1) + x->smpls[0].stamp = now; + if (stompSend((const char *)arg1->ring) != 0) + *(Boolean *)x->ring = B_FALSE; + else + *(Boolean *)x->ring = B_TRUE; + perf->actions++; + } +} + + +/* + * action argument handling ... including %h, %v and %i substitution + */ +void +actArg(Expr *x) +{ + Expr *sp = x->arg1; + char *string = (char *)0; + size_t length = 0; + + for (sp = x->arg1; sp != NULL; sp = sp->arg1) + length = formatSatisfyingValue((char *)sp->ring, length, &string); + + newStringBfr(x, length, string); +} + + +/* + * fake actions for archive mode + */ +void +actFake(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + time_t clock = (time_t)now; + char bfr[26]; + + if ((arg2 == NULL) || + (x->smpls[0].stamp == 0) || + (now >= *(RealTime *)arg2->ring + x->smpls[0].stamp)) + { + EVALARG(arg1) + *(Boolean *)x->ring = B_TRUE; + x->smpls[0].stamp = now; + pmCtime(&clock, bfr); + bfr[24] = '\0'; + printf("%s %s: %s\n", opStrings(x->op), bfr, (char *)arg1->ring); + } +} + diff --git a/src/pmie/src/aggregate.sk b/src/pmie/src/aggregate.sk new file mode 100644 index 0000000..98bf4c0 --- /dev/null +++ b/src/pmie/src/aggregate.sk @@ -0,0 +1,176 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * skeleton: aggregate.sk - aggregation and quantification + ***********************************************************************/ + +/*********************************************************************** + * operator: @FUN + ***********************************************************************/ + +void +@FUN_host(Expr *x) +{ + Expr *arg1 = x->arg1; + Sample *is = &arg1->smpls[0]; + Sample *os = &x->smpls[0]; + @ITYPE *ip; + @OTYPE *op; + @TTYPE a; + int n; + int i; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid && arg1->hdom > 0) { + ip = (@ITYPE *)is->ptr; + op = (@OTYPE *)os->ptr; + n = arg1->hdom; + @TOP + for (i = 1; i < n; i++) { + ip++; + @LOOP + } + @BOT + os->stamp = is->stamp; + x->valid++; + } + else { + x->valid = 0; + } + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_host(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +@FUN_inst(Expr *x) +{ + Expr *arg1 = x->arg1; + Sample *is = &arg1->smpls[0]; + Sample *os = &x->smpls[0]; + @ITYPE *ip; + @OTYPE *op; + @TTYPE a; + Metric *m; + int n; + int i, j; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid && x->hdom != 0) { + ip = (@ITYPE *)is->ptr; + op = (@OTYPE *)os->ptr; + if (abs(x->hdom) == 1) { + n = arg1->e_idom; + if (n < 1) { + @NOTVALID + goto done; + } + @TOP + for (i = 1; i < n; i++) { + ip++; + @LOOP + } + @BOT + } + else { + m = x->metrics; + for (i = 0; i < x->hdom; i++) { + n = m->m_idom; + if (n < 1) { + @NOTVALID + goto done; + } + @TOP + for (j = 1; j < n; j++){ + /* Note! no break allowed in this loop */ + ip++; + @LOOP + } + @BOT + m++; + } + } + os->stamp = is->stamp; + x->valid++; + } + else { + x->valid = 0; + } + +done: +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_inst(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +@FUN_time(Expr *x) +{ + Expr *arg1 = x->arg1; + Sample *is = &arg1->smpls[0]; + Sample *os = &x->smpls[0]; + @ITYPE *ring = (@ITYPE *)arg1->ring; + @ITYPE *ip; + @OTYPE *op; + @TTYPE a; + int n = arg1->tdom; + int tspan; + int i, j; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid >= n && x->tspan > 0 && arg1->tdom > 0) { + op = (@OTYPE *)os->ptr; + tspan = x->tspan; + for (i = 0; i < tspan; i++) { + ip = ring + i; + @TOP + for (j = 1; j < n; j++){ + ip += tspan; + @LOOP + } + @BOT + } + os->stamp = is->stamp; + x->valid++; + } + else { + x->valid = 0; + } + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_time(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + diff --git a/src/pmie/src/andor.c b/src/pmie/src/andor.c new file mode 100644 index 0000000..d0e2696 --- /dev/null +++ b/src/pmie/src/andor.c @@ -0,0 +1,457 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * andor.c + * + * These functions were originally generated from skeletons <file>.sk + * by the shell-script './meta', then modified to support the semantics + * of the boolean AND/OR operators correctly. These are different to + * every other operator in that they do not always require both sides + * of the expression to be available in order to be evaluated, i.e. + * OR: if either side of the expression is true, expr is true + * AND: if either side of the expression is false, expr is false + ***********************************************************************/ + +#include "pmapi.h" +#include "dstruct.h" +#include "pragmatics.h" +#include "fun.h" +#include "show.h" +#include "stomp.h" + + +/* + * operator: cndOr + */ + +#define OR(x,y) (((x) == B_TRUE || (y) == B_TRUE) ? B_TRUE : (((x) == B_FALSE && (y) == B_FALSE) ? B_FALSE : B_UNKNOWN)) +#define OR1(x) ((x) == B_TRUE ? B_TRUE : B_UNKNOWN) + +void +cndOr_n_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + Boolean *ip1; + Boolean *ip2; + Boolean *op; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0) { + ip1 = (Boolean *)is1->ptr; + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = OR(*ip1, *ip2); + ip1++; + ip2++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid && x->tspan > 0) { + ip1 = (Boolean *)is1->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = OR1(*ip1); + ip1++; + } + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid && x->tspan > 0) { + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = OR1(*ip2); + ip2++; + } + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndOr_n_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +cndOr_n_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + Boolean *ip1; + Boolean iv2; + Boolean *op; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0) { + ip1 = (Boolean *)is1->ptr; + iv2 = *(Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = OR(*ip1, iv2); + ip1++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid && x->tspan > 0) { + ip1 = (Boolean *)is1->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = OR1(*ip1); + ip1++; + } + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid && x->tspan > 0) { + *(Boolean *)os->ptr = OR1(*(Boolean *)is2->ptr); + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndOr_n_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +cndOr_1_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + Boolean iv1; + Boolean *ip2; + Boolean *op; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0) { + iv1 = *(Boolean *)is1->ptr; + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = OR(iv1, *ip2); + ip2++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid && x->tspan > 0) { + *(Boolean *)os->ptr = OR1(*(Boolean *)is1->ptr); + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid && x->tspan > 0) { + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = OR1(*ip2); + ip2++; + } + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndOr_1_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +cndOr_1_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid) { + *(Boolean *)os->ptr = OR(*(Boolean *)is1->ptr, *(Boolean *)is2->ptr); + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid) { + *(Boolean *)os->ptr = OR1(*(Boolean *)is1->ptr); + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid) { + *(Boolean *)os->ptr = OR1(*(Boolean *)is2->ptr); + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndOr_1_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +/* + * operator: cndAnd + */ + +#define AND(x,y) (((x) == B_TRUE && (y) == B_TRUE) ? B_TRUE : (((x) == B_FALSE || (y) == B_FALSE) ? B_FALSE : B_UNKNOWN)) +#define AND1(x) (((x) == B_FALSE) ? B_FALSE : B_UNKNOWN) + +void +cndAnd_n_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + Boolean *ip1; + Boolean *ip2; + Boolean *op; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid) { + ip1 = (Boolean *)is1->ptr; + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = AND(*ip1, *ip2); + ip1++; + ip2++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid) { + ip1 = (Boolean *)is1->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = AND1(*ip1); + ip1++; + } + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid) { + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = AND1(*ip2); + ip2++; + } + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndAnd_n_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +cndAnd_n_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + Boolean *ip1; + Boolean iv2; + Boolean *op; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0) { + ip1 = (Boolean *)is1->ptr; + iv2 = *(Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = AND(*ip1, iv2); + ip1++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid && x->tspan > 0) { + ip1 = (Boolean *)is1->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = AND1(*ip1); + ip1++; + } + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid && x->tspan > 0) { + *(Boolean *)os->ptr = AND1(*(Boolean *)is2->ptr); + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndAnd_n_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +cndAnd_1_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + Boolean iv1; + Boolean *ip2; + Boolean *op; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0) { + iv1 = *(Boolean *)is1->ptr; + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = AND(iv1, *ip2); + ip2++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid && x->tspan > 0) { + *(Boolean *)os->ptr = AND1(*(Boolean *)is1->ptr); + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid && x->tspan > 0) { + ip2 = (Boolean *)is2->ptr; + op = (Boolean *)os->ptr; + for (i = 0; i < x->tspan; i++) { + *op++ = AND1(*ip2); + ip2++; + } + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndAnd_1_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +cndAnd_1_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid) { + *(Boolean *)os->ptr = AND(*(Boolean *)is1->ptr, *(Boolean *)is2->ptr); + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else if (arg1->valid) { + *(Boolean *)os->ptr = AND1(*(Boolean *)is1->ptr); + os->stamp = is1->stamp; + x->valid++; + } + else if (arg2->valid) { + *(Boolean *)os->ptr = AND1(*(Boolean *)is2->ptr); + os->stamp = is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndAnd_1_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} diff --git a/src/pmie/src/andor.h b/src/pmie/src/andor.h new file mode 100644 index 0000000..bc213b8 --- /dev/null +++ b/src/pmie/src/andor.h @@ -0,0 +1,34 @@ +/*********************************************************************** + * andor.h - Logical AND/OR expression evaluator functions + *********************************************************************** + * + * 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 ANDOR_H +#define ANDOR_H + +/* expression evaluator function prototypes */ +void cndOr_n_n(Expr *); +void cndOr_1_n(Expr *); +void cndOr_n_1(Expr *); +void cndOr_1_1(Expr *); +void cndAnd_n_n(Expr *); +void cndAnd_1_n(Expr *); +void cndAnd_n_1(Expr *); +void cndAnd_1_1(Expr *); + +#endif /* ANDOR_H */ diff --git a/src/pmie/src/binary.sk b/src/pmie/src/binary.sk new file mode 100644 index 0000000..26ada2a --- /dev/null +++ b/src/pmie/src/binary.sk @@ -0,0 +1,179 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * skeleton: binary.sk - binary operator + ***********************************************************************/ + +/* + * operator: @FUN + */ + +#define @OP + +void +@FUN_n_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + @ITYPE *ip1; + @ITYPE *ip2; + @OTYPE *op; + int n; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0 && x->tspan == arg1->tspan && x->tspan == arg2->tspan) { + ip1 = (@ITYPE *)is1->ptr; + ip2 = (@ITYPE *)is2->ptr; + op = (@OTYPE *)os->ptr; + n = x->tspan; + for (i = 0; i < n; i++) { + *op++ = OP(*ip1, *ip2); + ip1++; + ip2++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_n_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +@FUN_n_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + @ITYPE *ip1; + @ITYPE iv2; + @OTYPE *op; + int n; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0 && x->tspan == arg1->tspan) { + ip1 = (@ITYPE *)is1->ptr; + iv2 = *(@ITYPE *)is2->ptr; + op = (@OTYPE *)os->ptr; + n = x->tspan; + for (i = 0; i < n; i++) { + *op++ = OP(*ip1, iv2); + ip1++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_n_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +@FUN_1_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + @ITYPE iv1; + @ITYPE *ip2; + @OTYPE *op; + int n; + int i; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid && x->tspan > 0 && x->tspan == arg2->tspan) { + iv1 = *(@ITYPE *)is1->ptr; + ip2 = (@ITYPE *)is2->ptr; + op = (@OTYPE *)os->ptr; + n = x->tspan; + for (i = 0; i < n; i++) { + *op++ = OP(iv1, *ip2); + ip2++; + } + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_1_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +@FUN_1_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg2->smpls[0]; + Sample *os = &x->smpls[0]; + + EVALARG(arg1) + EVALARG(arg2) + ROTATE(x) + + if (arg1->valid && arg2->valid) { + *(@OTYPE *)os->ptr = OP(*(@ITYPE *)is1->ptr, *(@ITYPE *)is2->ptr); + os->stamp = (is1->stamp > is2->stamp) ? is1->stamp : is2->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_1_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +#undef OP + diff --git a/src/pmie/src/dstruct.c b/src/pmie/src/dstruct.c new file mode 100644 index 0000000..5d9b8bb --- /dev/null +++ b/src/pmie/src/dstruct.c @@ -0,0 +1,1307 @@ +/*********************************************************************** + * dstruct.c - central data structures and associated operations + *********************************************************************** + * + * Copyright (c) 2013-2014 Red Hat. + * 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 "pmapi.h" +#include "impl.h" +#include <math.h> +#include <ctype.h> +#include <limits.h> +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#include "dstruct.h" +#include "symbol.h" +#include "pragmatics.h" +#include "fun.h" +#include "eval.h" +#include "show.h" + +#if defined(HAVE_VALUES_H) +#include <values.h> +#endif +#if defined(HAVE_IEEEFP_H) +#include <ieeefp.h> +#endif + +/*********************************************************************** + * constants + ***********************************************************************/ + +double mynan; /* not-a-number run time initialized */ + + +/*********************************************************************** + * user supplied parameters + ***********************************************************************/ + +char *pmnsfile = PM_NS_DEFAULT; /* alternate name space */ +Archive *archives; /* list of open archives */ +RealTime first = -1; /* archive starting point */ +RealTime last = 0.0; /* archive end point */ +char *dfltHostConn; /* default host connection string */ +char *dfltHostName; /* default host name */ +RealTime dfltDelta = DELTA_DFLT; /* default sample interval */ +char *startFlag; /* start time specified? */ +char *stopFlag; /* end time specified? */ +char *alignFlag; /* align time specified? */ +char *offsetFlag; /* offset time specified? */ +RealTime runTime; /* run time interval */ +int hostZone; /* timezone from host? */ +char *timeZone; /* timezone from command line */ +int verbose; /* verbosity 0, 1 or 2 */ +int interactive; /* interactive mode, -d */ +int isdaemon; /* run as a daemon */ +int agent; /* secret agent mode? */ +int applet; /* applet mode? */ +int dowrap; /* counter wrap? default no */ +int doexit; /* time to exit stage left? */ +int dorotate; /* is a log rotation pending? */ +pmiestats_t *perf; /* live performance data */ +pmiestats_t instrument; /* used if no mmap (archive) */ + + +/*********************************************************************** + * this is where the action is + ***********************************************************************/ + +Task *taskq = NULL; /* evaluator task queue */ +Expr *curr; /* current executing rule expression */ + +SymbolTable hosts; /* currently known hosts */ +SymbolTable metrics; /* currently known metrics */ +SymbolTable rules; /* currently known rules */ +SymbolTable vars; /* currently known variables */ + + +/*********************************************************************** + * time + ***********************************************************************/ + +RealTime now; /* current time */ +RealTime start; /* start evaluation time */ +RealTime stop; /* stop evaluation time */ + +Symbol symDelta; /* current sample interval */ +Symbol symMinute; /* minutes after the hour 0..59 */ +Symbol symHour; /* hours since midnight 0..23 */ +Symbol symDay; /* day of the month 1..31 */ +Symbol symMonth; /* month of the year 1..12 */ +Symbol symYear; /* year 1996.. */ +Symbol symWeekday; /* days since Sunday 0..6 */ + +static double delta; /* current sample interval */ +static double second; /* seconds after the minute 0..59 */ +static double minute; /* minutes after the hour 0..59 */ +static double hour; /* hours since midnight 0..23 */ +static double day; /* day of the month 1..31 */ +static double month; /* month of the year 1..12 */ +static double year; /* year 1996.. */ +static double weekday; /* days since Sunday 0..6 */ + +/*********************************************************************** + * process creation control + ***********************************************************************/ +int need_wait; + +/* return real time */ +RealTime +getReal(void) +{ + struct timeval t; + + __pmtimevalNow(&t); + return realize(t); +} + + +/* update time variables to reflect current time */ +void +reflectTime(RealTime d) +{ + static time_t then = 0; /* previous time */ + int skip = now - then; + struct tm tm; + + then = (time_t)now; + + /* sample interval */ + delta = d; + + /* try short path for current time */ + if (skip >= 0 && skip < 24 * 60 * 60) { + second += skip; + if (second < 60) + return; + skip = (int)(second / 60); + second -= (double)(60 * skip); + minute += (double)skip; + if (minute < 60) + return; + skip = (int)(minute / 60); + minute -= (double)(60 * skip); + hour += (double)skip; + if (hour < 24) + return; + } + + /* long path for current time */ + pmLocaltime(&then, &tm); + second = (double) tm.tm_sec; + minute = (double) tm.tm_min; + hour = (double) tm.tm_hour; + day = (double) tm.tm_mday; + month = (double) tm.tm_mon; + /* tm_year is years since 1900, so this is Y2K safe */ + year = (double) tm.tm_year + 1900; + weekday = (double) tm.tm_wday; +} + + +/* convert RealTime to timeval */ +void +unrealize(RealTime rt, struct timeval *tv) +{ + tv->tv_sec = (time_t)rt; + tv->tv_usec = (int)(1000000 * (rt - tv->tv_sec)); +} + + +/* convert RealTime to timespec */ +void +unrealizenano(RealTime rt, struct timespec *ts) +{ + ts->tv_sec = (time_t)rt; + ts->tv_nsec = (int)(1000000000 * (rt - ts->tv_sec)); +} + +#define SLEEP_EVAL 0 +#define SLEEP_RETRY 1 + +/* sleep until eval or retry RealTime */ +void +sleepTight(Task *t, int type) +{ + RealTime sched; + RealTime delay; /* interval to sleep */ + int sts; + RealTime cur_entry = getReal(); +#ifdef HAVE_WAITPID + pid_t pid; + + if (need_wait) { + /* harvest terminated children */ + while ((pid = waitpid(-1, &sts, WNOHANG)) > (pid_t)0) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "sleepTight: wait: pid=%" FMT_PID " done status=0x%x", pid, sts); + if (WIFEXITED(sts)) + fprintf(stderr, " exit=%d", WEXITSTATUS(sts)); + if (WIFSIGNALED(sts)) + fprintf(stderr, " signal=%d", WTERMSIG(sts)); + fprintf(stderr, "\n"); + } +#endif + ; + } + need_wait = 0; + } +#endif + + if (!archives) { + struct timespec ts, tleft; + static RealTime last_sched = -1; + static Task *last_t; + static int last_type; + RealTime cur = getReal(); + + sched = type == SLEEP_EVAL ? t->eval : t->retry; + + delay = sched - cur; + if (delay < 0) { + int show_detail = 0; + if (delay <= -1) { + fprintf(stderr, "sleepTight: negative delay (%f). sched=%f, cur=%f\n", + delay, sched, cur); + show_detail = 1; + } +#if PCP_DEBUG + else { + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "sleepTight: small negative delay (%f). sched=%f, cur=%f\n", + delay, sched, cur); + show_detail = 1; + } + } +#endif + if (show_detail) { + if (last_sched > 0) { + fprintf(stderr, "Last sleepTight (%s) until: ", last_type == SLEEP_EVAL ? "eval" : "retry"); + showFullTime(stderr, last_sched); + fputc('\n', stderr); + fprintf(stderr, "Last "); + dumpTask(last_t); + } + fprintf(stderr, "This sleepTight() entry: "); + showFullTime(stderr, cur_entry); + fputc('\n', stderr); + fprintf(stderr, "Harvest children done: "); + showFullTime(stderr, cur); + fputc('\n', stderr); + fprintf(stderr, "Want sleepTight (%s) until: ", type == SLEEP_EVAL ? "eval" : "retry"); + showFullTime(stderr, sched); + fputc('\n', stderr); + fprintf(stderr, "This "); + dumpTask(t); + } + } + else { + unrealizenano(delay, &ts); + for (;;) { /* loop to catch early wakeup from nanosleep */ + if (ts.tv_sec < 0 || ts.tv_nsec > 999999999) { + fprintf(stderr, "sleepTight: invalid args: %ld %ld\n", + ts.tv_sec, ts.tv_nsec); + break; + } + sts = nanosleep(&ts, &tleft); + /* deferred signal handling done immediately */ + if (doexit) + exit(doexit); + if (dorotate) { + logRotate(); + dorotate = 0; + } + if (sts == 0 || (sts < 0 && oserror() != EINTR)) + break; + ts = tleft; + } + } + last_t = t; + last_type = type; + last_sched = sched; + } +} + + +/*********************************************************************** + * ring buffer management + ***********************************************************************/ + +void +newRingBfr(Expr *x) +{ + size_t sz; + char *p; + int i; + + sz = ((x->sem == SEM_BOOLEAN) || (x->sem == SEM_CHAR)) ? + sizeof(char) * x->tspan : + sizeof(double) * x->tspan; + if (x->ring) free(x->ring); + x->ring = alloc(x->nsmpls * sz); + p = (char *) x->ring; + for (i = 0; i < x->nsmpls; i++) { + x->smpls[i].ptr = (void *) p; + p += sz; + } +} + + +void +newStringBfr(Expr *x, size_t length, char *bfr) +{ + if (x->e_idom != (int)length) { + x->e_idom = (int)length; + x->tspan = (int)length; + x->nvals = (int)length; + } + if (x->ring) + free(x->ring); + x->ring = bfr; + x->smpls[0].ptr = (void *) bfr; +} + + +/* Rotate ring buffer - safe to call only if x->nsmpls > 1 */ +void +rotate(Expr *x) +{ + int n = x->nsmpls-1; + Sample *q = &x->smpls[n]; + Sample *p = q - 1; + void *t = q->ptr; + int i; + + for (i = n; i > 0; i--) + *q-- = *p--; + x->smpls[0].ptr = t; +} + + +/*********************************************************************** + * memory allocation + ***********************************************************************/ + +void * +alloc(size_t size) +{ + void *p; + + if ((p = malloc(size)) == NULL) { + __pmNoMem("pmie.alloc", size, PM_FATAL_ERR); + } + return p; +} + + +void * +zalloc(size_t size) +{ + void *p; + + if ((p = calloc(1, size)) == NULL) { + __pmNoMem("pmie.zalloc", size, PM_FATAL_ERR); + } + return p; +} + + +void * +aalloc(size_t align, size_t size) +{ + void *p; + int sts = 0; +#ifdef HAVE_POSIX_MEMALIGN + sts = posix_memalign(&p, align, size); +#else +#ifdef HAVE_MEMALIGN + p = memalign(align, size); + if (p == NULL) sts = -1; +#else + p = malloc(size); + if (p == NULL) sts = -1; +#endif +#endif + if (sts != 0) { + __pmNoMem("pmie.aalloc", size, PM_FATAL_ERR); + } + return p; +} + + +void * +ralloc(void *p, size_t size) +{ + void *q; + + if ((q = realloc(p, size)) == NULL) { + __pmNoMem("pmie.ralloc", size, PM_FATAL_ERR); + } + return q; +} + +char * +sdup(char *p) +{ + char *q; + + if ((q = strdup(p)) == NULL) { + __pmNoMem("pmie.sdup", strlen(p), PM_FATAL_ERR); + } + return q; +} + + +Expr * +newExpr(int op, Expr *arg1, Expr *arg2, + int hdom, int idom, int tdom, int nsmpls, + int sem) +{ + Expr *x; + Expr *arg; + + x = (Expr *) zalloc(sizeof(Expr) + (nsmpls - 1) * sizeof(Sample)); + x->op = op; + if (arg1) { + x->arg1 = arg1; + arg1->parent = x; + } + if (arg2) { + x->arg2 = arg2; + arg2->parent = x; + } + x->hdom = hdom; + x->e_idom = idom; + x->tdom = tdom; + x->nsmpls = nsmpls; + x->tspan = (x->e_idom >= 0) ? x->e_idom : abs(x->hdom); + x->nvals = x->tspan * nsmpls; + if (arg1) { + arg = primary(arg1, arg2); + x->metrics = arg->metrics; + } + if (sem == SEM_NUMVAR || sem == SEM_NUMCONST || sem == SEM_BOOLEAN || + sem == SEM_CHAR || sem == SEM_REGEX) + x->units = noUnits; + else { + x->units = noUnits; + SET_UNITS_UNKNOWN(x->units); + } + x->sem = sem; + return x; +} + + +Profile * +newProfile(Fetch *owner, pmInDom indom) +{ + Profile *p = (Profile *) zalloc(sizeof(Profile)); + p->indom = indom; + p->fetch = owner; + return p; +} + + +Fetch * +newFetch(Host *owner) +{ + Fetch *f = (Fetch *) zalloc(sizeof(Fetch)); + f->host = owner; + return f; +} + + +Host * +newHost(Task *owner, Symbol name) +{ + Host *h = (Host *) zalloc(sizeof(Host)); + + h->name = symCopy(name); + h->task = owner; + return h; +} + + +Task * +newTask(RealTime delta, int nth) +{ + Task *t = (Task *) zalloc(sizeof(Task)); + t->nth = nth; + t->delta = delta; + return t; +} + +/* translate new metric name to internal pmid for agent mode */ +static pmID +agentId(char *name) +{ + int sts; + pmID pmid; + + if ((sts = pmLookupName(1, &name, &pmid)) < 0) { + fprintf(stderr, "%s: agentId: metric %s not found in namespace: %s\n", + pmProgname, name, pmErrStr(sts)); + exit(1); + } + return pmid; +} + + +void +newResult(Task *t) +{ + pmResult *rslt; + Symbol *sym; + pmValueSet *vset; + pmValueBlock *vblk; + int i; + int len; + + /* allocate pmResult */ + rslt = (pmResult *) zalloc(sizeof(pmResult) + (t->nrules - 1) * sizeof(pmValueSet *)); + rslt->numpmid = t->nrules; + + /* allocate pmValueSet's */ + sym = t->rules; + for (i = 0; i < t->nrules; i++) { + vset = (pmValueSet *)alloc(sizeof(pmValueSet)); + vset->pmid = agentId(symName(*sym)); + vset->numval = 0; + vset->valfmt = PM_VAL_DPTR; + vset->vlist[0].inst = PM_IN_NULL; + len = PM_VAL_HDR_SIZE + sizeof(double); + vblk = (pmValueBlock *)zalloc(len); + vblk->vlen = len; + vblk->vtype = PM_TYPE_DOUBLE; + vset->vlist[0].value.pval = vblk; + rslt->vset[i] = vset; + sym++; + } + + t->rslt = rslt; +} + + +/*********************************************************************** + * memory deallocation + * + * IMPORTANT: These functions free the argument structure plus any + * structures it owns below it in the expression tree. + ***********************************************************************/ + +void +freeTask(Task *t) +{ + if ((t->hosts == NULL) && (t->rules == NULL)) { + if (t->next) t->next->prev = t->prev; + if (t->prev) t->prev->next = t->next; + else taskq = t->next; + free(t); + } +} + + +void +freeHost(Host *h) +{ + if ((h->fetches == NULL) && (h->waits == NULL)) { + if (h->next) h->next->prev = h->prev; + if (h->prev) h->prev->next = h->next; + else { + h->task->hosts = h->next; + freeTask(h->task); + } + symFree(h->name); + free(h); + } +} + + +void +freeFetch(Fetch *f) +{ + if (f->profiles == NULL) { + if (f->next) f->next->prev = f->prev; + if (f->prev) f->prev->next = f->next; + else { + f->host->fetches = f->next; + freeHost(f->host); + } + pmDestroyContext(f->handle); + if (f->result) pmFreeResult(f->result); + if (f->pmids) free(f->pmids); + free(f); + } +} + + +void +FreeProfile(Profile *p) +{ + if (p->metrics == NULL) { + if (p->next) p->next->prev = p->prev; + if (p->prev) p->prev->next = p->next; + else { + p->fetch->profiles = p->next; + freeFetch(p->fetch); + } + free(p); + } +} + + +void +freeMetric(Metric *m) +{ + int numinst; + + /* Metric is on fetch list */ + if (m->profile) { + if (m->prev) { + m->prev->next = m->next; + if (m->next) m->next->prev = m->prev; + } + else { + m->host->waits = m->next; + if (m->next) m->next->prev = NULL; + } + if (m->host) freeHost(m->host); + } + + symFree(m->hname); + numinst = m->specinst == 0 ? m->m_idom : m->specinst; + if (numinst > 0 && m->inames) { + int i; + for (i = 0; i < numinst; i++) { + if (m->inames[i] != NULL) free(m->inames[i]); + } + free(m->inames); + } + if (numinst && m->iids) free(m->iids); + if (m->vals) free(m->vals); +} + + +void +freeExpr(Expr *x) +{ + Metric *m; + int i; + + if (x) { + if (x->arg1 && x->arg1->parent == x) + freeExpr(x->arg1); + if (x->arg2 && x->arg2->parent == x) + freeExpr(x->arg2); + if (x->metrics && x->op == CND_FETCH) { + for (m = x->metrics, i = 0; i < x->hdom; m++, i++) + freeMetric(m); + /* + * x->metrics allocated in a block, one element per host, so + * free as one after all other freeing has been done. + */ + free(x->metrics); + } + if (x->ring) free(x->ring); + free(x); + } +} + + +/*********************************************************************** + * comparison functions (for use by qsort) + ***********************************************************************/ + +/* Compare two instance identifiers. + - This function is passed as an argument to qsort, hence the casts. */ +int /* -1 less, 0 equal, 1 greater */ +compid(const void *i1, const void *i2) +{ + if (*(int *)i1 < *(int *)i2) return -1; + if (*(int *)i1 > *(int *)i2) return 1; + return 0; +} + + +/* Compare two pmValue's on their inst fields + - This function is passed as an argument to qsort, hence the casts. */ +int /* -1 less, 0 equal, 1 greater */ +compair(const void *pmv1, const void *pmv2) +{ + if (((pmValue *)pmv1)->inst < ((pmValue *)pmv2)->inst) return -1; + if (((pmValue *)pmv1)->inst > ((pmValue *)pmv2)->inst) return 1; + return 0; +} + + +/*********************************************************************** + * Expr manipulation + ***********************************************************************/ + +/* Decide primary argument for inheritance of Expr attributes */ +Expr * +primary(Expr *arg1, Expr *arg2) +{ + if (arg2 == NULL || arg1->nvals > 1) + return arg1; + if (arg2->nvals > 1) + return arg2; + if (arg1->metrics && + (arg1->hdom != -1 || arg1->e_idom != -1 || arg1->tdom != -1)) + return arg1; + if (arg2->metrics && + (arg2->hdom != -1 || arg2->e_idom != -1 || arg2->tdom != -1)) + return arg2; + return arg1; +} + + +/* change number of samples allocated in ring buffer */ +void +changeSmpls(Expr **p, int nsmpls) +{ + Expr *x = *p; + Metric *m; + int i; + + if (nsmpls == x->nsmpls) return; + *p = x = (Expr *) ralloc(x, sizeof(Expr) + (nsmpls - 1) * sizeof(Sample)); + x->nsmpls = nsmpls; + x->nvals = x->tspan * nsmpls; + x->valid = 0; + if (x->op == CND_FETCH) { + m = x->metrics; + for (i = 0; i < x->hdom; i++) { + m->expr = x; + m++; + } + } + newRingBfr(x); +} + + +/* propagate instance domain, semantics and units from + argument expressions to parents */ +static void +instExpr(Expr *x) +{ + int up = 0; + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Expr *arg = primary(arg1, arg2); + + /* semantics ... */ + if (x->sem == SEM_UNKNOWN) { + if (arg2 == NULL) { + /* unary expression */ + if (arg1->sem != SEM_UNKNOWN) { + up = 1; + x->sem = arg1->sem; + } + } + else if ((arg1->sem != SEM_UNKNOWN) && + (arg2->sem != SEM_UNKNOWN)) { + /* binary expression with known args */ + up = 1; + x->sem = arg->sem; + } + /* binary expression with unknown arg */ + else + return; + } + + /* units ... */ + if (UNITS_UNKNOWN(x->units)) { + if (arg2 == NULL) { + /* unary expression */ + if (!UNITS_UNKNOWN(arg1->units)) { + up = 1; + x->units = arg1->units; + } + } + else if (!UNITS_UNKNOWN(arg1->units) && + !UNITS_UNKNOWN(arg2->units)) { + /* binary expression with known args */ + up = 1; + x->units = arg->units; + if (x->op == CND_MUL) { + x->units.dimSpace = arg1->units.dimSpace + arg2->units.dimSpace; + x->units.dimTime = arg1->units.dimTime + arg2->units.dimTime; + x->units.dimCount = arg1->units.dimCount + arg2->units.dimCount; + } + else if (x->op == CND_DIV) { + x->units.dimSpace = arg1->units.dimSpace - arg2->units.dimSpace; + x->units.dimTime = arg1->units.dimTime - arg2->units.dimTime; + x->units.dimCount = arg1->units.dimCount - arg2->units.dimCount; + } + } + } + + /* instance domain */ + if ((x->e_idom != -1) && (x->e_idom != arg->e_idom)) { + up = 1; + x->e_idom = arg->e_idom; + x->tspan = (x->e_idom >= 0) ? x->e_idom : abs(x->hdom); + x->nvals = x->tspan * x->nsmpls; + x->valid = 0; + newRingBfr(x); + } + + if (up && x->parent) + instExpr(x->parent); +} + + +/* propagate instance domain, semantics and units from given + fetch expression to its parents */ +void +instFetchExpr(Expr *x) +{ + Metric *m; + int ninst; + int up = 0; + int i; + + /* update semantics and units */ + if (x->sem == SEM_UNKNOWN) { + m = x->metrics; + for (i = 0; i < x->hdom; i++) { + if (m->desc.sem != SEM_UNKNOWN) { + if (m->desc.sem == PM_SEM_COUNTER) { + x->sem = PM_SEM_INSTANT; + x->units = canon(m->desc.units); + x->units.dimTime--; + } + else { + x->sem = m->desc.sem; + x->units = canon(m->desc.units); + } + up = 1; + break; + } + } + } + + /* + * update number of instances ... need to be careful because may be more + * than one host, and instances may not be fully available ... + * m_idom < 0 => no idea how many instances there might be (cannot + * contact pmcd, unknown metric, can't get indom, ...) + * m_idom == 0 => no values, but otherwise OK + * m_idom > 0 => have this many values (and hence instances) + */ + m = x->metrics; + ninst = -1; + for (i = 0; i < x->hdom; i++) { + m->offset = ninst; + if (m->m_idom >= 0) { + if (ninst == -1) + ninst = m->m_idom; + else + ninst += m->m_idom; + } + m++; + } + if (x->e_idom != ninst) { + /* number of instances is different */ + x->e_idom = ninst; + x->tspan = (x->e_idom >= 0) ? x->e_idom : abs(x->hdom); + x->nvals = x->nsmpls * x->tspan; + x->valid = 0; + newRingBfr(x); + up = 1; + } + if (x->parent) { + /* do we need to propagate changes? */ + if (up || + (UNITS_UNKNOWN(x->parent->units) && !UNITS_UNKNOWN(x->units))) { + instExpr(x->parent); + } + } +} + + +/*********************************************************************** + * compulsory initialization + ***********************************************************************/ + +void dstructInit(void) +{ + Expr *x; + double zero = 0.0; + + /* not-a-number initialization */ + mynan = zero / zero; + + /* don't initialize dfltHost*; let pmie.c do it after getopt. */ + + /* set up symbol tables */ + symSetTable(&hosts); + symSetTable(&metrics); + symSetTable(&rules); + symSetTable(&vars); + + /* set yp inter-sample interval (delta) symbol */ + symDelta = symIntern(&vars, "delta"); + x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR); + x->smpls[0].ptr = δ + x->valid = 1; + symValue(symDelta) = x; + + /* set up time symbols */ + symMinute = symIntern(&vars, "minute"); + x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR); + x->smpls[0].ptr = &minute; + x->valid = 1; + symValue(symMinute) = x; + symHour = symIntern(&vars, "hour"); + x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR); + x->smpls[0].ptr = &hour; + x->valid = 1; + symValue(symHour) = x; + symDay = symIntern(&vars, "day"); + x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR); + x->smpls[0].ptr = &day; + x->valid = 1; + symValue(symDay) = x; + symMonth = symIntern(&vars, "month"); + x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR); + x->smpls[0].ptr = &month; + x->valid = 1; + symValue(symMonth) = x; + symYear = symIntern(&vars, "year"); + x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR); + x->smpls[0].ptr = &year; + x->valid = 1; + symValue(symYear) = x; + symWeekday = symIntern(&vars, "day_of_week"); + x = newExpr(OP_VAR, NULL,NULL, -1, -1, -1, 1, SEM_NUMVAR); + x->smpls[0].ptr = &weekday; + x->valid = 1; + symValue(symWeekday) = x; +} + + +/* get ready to run evaluator */ +void +agentInit(void) +{ + Task *t; + int sts; + + /* Set up local name space for agent */ + /* Only load PMNS if it's default and hence not already loaded */ + if (pmnsfile == PM_NS_DEFAULT && (sts = pmLoadNameSpace(pmnsfile)) < 0) { + fprintf(stderr, "%s: agentInit: cannot load metric namespace: %s\n", + pmProgname, pmErrStr(sts)); + exit(1); + } + + /* allocate pmResult's and send pmDescs for secret agent mode */ + t = taskq; + while (t) { + newResult(t); + sendDescs(t); + t = t->next; + } +} + +/* + * useful for diagnostics and with dbx + */ + +static struct { + void (*addr)(Expr *); + char *name; +} fn_map[] = { + { actAlarm, "actAlarm" }, + { actAnd, "actAnd" }, + { actArg, "actArg" }, + { actFake, "actFake" }, + { actOr, "actOr" }, + { actPrint, "actPrint" }, + { actShell, "actShell" }, + { actStomp, "actStomp" }, + { actSyslog, "actSyslog" }, + { cndAdd_1_1, "cndAdd_1_1" }, + { cndAdd_1_n, "cndAdd_1_n" }, + { cndAdd_n_1, "cndAdd_n_1" }, + { cndAdd_n_n, "cndAdd_n_n" }, + { cndAll_host, "cndAll_host" }, + { cndAll_inst, "cndAll_inst" }, + { cndAll_time, "cndAll_time" }, + { cndAnd_1_1, "cndAnd_1_1" }, + { cndAnd_1_n, "cndAnd_1_n" }, + { cndAnd_n_1, "cndAnd_n_1" }, + { cndAnd_n_n, "cndAnd_n_n" }, + { cndAvg_host, "cndAvg_host" }, + { cndAvg_inst, "cndAvg_inst" }, + { cndAvg_time, "cndAvg_time" }, + { cndCount_host, "cndCount_host" }, + { cndCount_inst, "cndCount_inst" }, + { cndCount_time, "cndCount_time" }, + { cndDelay_1, "cndDelay_1" }, + { cndDelay_n, "cndDelay_n" }, + { cndDiv_1_1, "cndDiv_1_1" }, + { cndDiv_1_n, "cndDiv_1_n" }, + { cndDiv_n_1, "cndDiv_n_1" }, + { cndDiv_n_n, "cndDiv_n_n" }, + { cndEq_1_1, "cndEq_1_1" }, + { cndEq_1_n, "cndEq_1_n" }, + { cndEq_n_1, "cndEq_n_1" }, + { cndEq_n_n, "cndEq_n_n" }, + { cndFall_1, "cndFall_1" }, + { cndFall_n, "cndFall_n" }, + { cndFetch_1, "cndFetch_1" }, + { cndFetch_all, "cndFetch_all" }, + { cndFetch_n, "cndFetch_n" }, + { cndGt_1_1, "cndGt_1_1" }, + { cndGt_1_n, "cndGt_1_n" }, + { cndGt_n_1, "cndGt_n_1" }, + { cndGt_n_n, "cndGt_n_n" }, + { cndGte_1_1, "cndGte_1_1" }, + { cndGte_1_n, "cndGte_1_n" }, + { cndGte_n_1, "cndGte_n_1" }, + { cndGte_n_n, "cndGte_n_n" }, + { cndLt_1_1, "cndLt_1_1" }, + { cndLt_1_n, "cndLt_1_n" }, + { cndLt_n_1, "cndLt_n_1" }, + { cndLt_n_n, "cndLt_n_n" }, + { cndLte_1_1, "cndLte_1_1" }, + { cndLte_1_n, "cndLte_1_n" }, + { cndLte_n_1, "cndLte_n_1" }, + { cndLte_n_n, "cndLte_n_n" }, + { cndMax_host, "cndMax_host" }, + { cndMax_inst, "cndMax_inst" }, + { cndMax_time, "cndMax_time" }, + { cndMin_host, "cndMin_host" }, + { cndMin_inst, "cndMin_inst" }, + { cndMin_time, "cndMin_time" }, + { cndMul_1_1, "cndMul_1_1" }, + { cndMul_1_n, "cndMul_1_n" }, + { cndMul_n_1, "cndMul_n_1" }, + { cndMul_n_n, "cndMul_n_n" }, + { cndNeg_1, "cndNeg_1" }, + { cndNeg_n, "cndNeg_n" }, + { cndNeq_1_1, "cndNeq_1_1" }, + { cndNeq_1_n, "cndNeq_1_n" }, + { cndNeq_n_1, "cndNeq_n_1" }, + { cndNeq_n_n, "cndNeq_n_n" }, + { cndNot_1, "cndNot_1" }, + { cndNot_n, "cndNot_n" }, + { cndOr_1_1, "cndOr_1_1" }, + { cndOr_1_n, "cndOr_1_n" }, + { cndOr_n_1, "cndOr_n_1" }, + { cndOr_n_n, "cndOr_n_n" }, + { cndPcnt_host, "cndPcnt_host" }, + { cndPcnt_inst, "cndPcnt_inst" }, + { cndPcnt_time, "cndPcnt_time" }, + { cndRate_1, "cndRate_1" }, + { cndRate_n, "cndRate_n" }, + { cndRise_1, "cndRise_1" }, + { cndRise_n, "cndRise_n" }, + { cndSome_host, "cndSome_host" }, + { cndSome_inst, "cndSome_inst" }, + { cndSome_time, "cndSome_time" }, + { cndSub_1_1, "cndSub_1_1" }, + { cndSub_1_n, "cndSub_1_n" }, + { cndSub_n_1, "cndSub_n_1" }, + { cndSub_n_n, "cndSub_n_n" }, + { cndSum_host, "cndSum_host" }, + { cndSum_inst, "cndSum_inst" }, + { cndSum_time, "cndSum_time" }, + { rule, "rule" }, + { NULL, NULL }, +}; + +static struct { + int val; + char *name; +} sem_map[] = { + { SEM_UNKNOWN, "UNKNOWN" }, + { SEM_NUMVAR, "NUMVAR" }, + { SEM_NUMCONST, "NUMCONST" }, + { SEM_BOOLEAN, "TRUTH" }, + { SEM_CHAR, "CHAR" }, + { SEM_REGEX, "REGEX" }, + { PM_SEM_COUNTER, "COUNTER" }, + { PM_SEM_INSTANT, "INSTANT" }, + { PM_SEM_DISCRETE, "DISCRETE" }, + { 0, NULL }, +}; + +void +__dumpExpr(int level, Expr *x) +{ + int i; + int j; + int k; + + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, "Expr dump @ " PRINTF_P_PFX "%p\n", x); + if (x == NULL) return; + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, " op=%d (%s) arg1=" PRINTF_P_PFX "%p arg2=" PRINTF_P_PFX "%p parent=" PRINTF_P_PFX "%p\n", + x->op, opStrings(x->op), x->arg1, x->arg2, x->parent); + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, " eval="); + for (j = 0; fn_map[j].addr; j++) { + if (x->eval == fn_map[j].addr) { + fprintf(stderr, "%s", fn_map[j].name); + break; + } + } + if (fn_map[j].addr == NULL) + fprintf(stderr, "" PRINTF_P_PFX "%p()", x->eval); + fprintf(stderr, " metrics=" PRINTF_P_PFX "%p ring=" PRINTF_P_PFX "%p\n", x->metrics, x->ring); + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, " valid=%d cardinality[H,I,T]=[%d,%d,%d] tspan=%d\n", + x->valid, x->hdom, x->e_idom, x->tdom, x->tspan); + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, " nsmpls=%d nvals=%d sem=", x->nsmpls, x->nvals); + for (j = 0; sem_map[j].name; j++) { + if (x->sem == sem_map[j].val) { + fprintf(stderr, "%s", sem_map[j].name); + break; + } + } + if (sem_map[j].name == NULL) + fprintf(stderr, "%d", x->sem); + if (UNITS_UNKNOWN(x->units)) + fprintf(stderr, " units=UNKNOWN\n"); + else + fprintf(stderr, " units=%s\n", pmUnitsStr(&x->units)); + if (x->valid > 0) { + if (x->sem == SEM_BOOLEAN || x->sem == SEM_CHAR || + x->sem == SEM_NUMVAR || x->sem == SEM_NUMCONST || + x->sem == PM_SEM_COUNTER || x->sem == PM_SEM_INSTANT || + x->sem == PM_SEM_DISCRETE) { + for (j = 0; j < x->nsmpls; j++) { + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, " smpls[%d].ptr " PRINTF_P_PFX "%p ", j, x->smpls[j].ptr); + for (k = 0; k < x->tspan; k++) { + if (x->tspan > 1 && x->sem != SEM_CHAR) { + if (k > 0) + fprintf(stderr, ", "); + fprintf(stderr, "{%d} ", k); + } + if (x->sem == SEM_BOOLEAN) { + char c = *((char *)x->smpls[j].ptr+k); + if ((int)c == B_TRUE) + fprintf(stderr, "true"); + else if ((int)c == B_FALSE) + fprintf(stderr, "false"); + else if ((int)c == B_UNKNOWN) + fprintf(stderr, "unknown"); + else + fprintf(stderr, "bogus (0x%x)", c & 0xff); + } + else if (x->sem == SEM_CHAR) { + if (k == 0) + fprintf(stderr, "\"%s\"", (char *)x->smpls[j].ptr); + } + else { + double v = *((double *)x->smpls[j].ptr+k); + int fp_bad = 0; +#ifdef HAVE_FPCLASSIFY + fp_bad = fpclassify(v) == FP_NAN; +#else +#ifdef HAVE_ISNAN + fp_bad = isnan(v); +#endif +#endif + if (fp_bad) + fputc('?', stderr); + else + fprintf(stderr, "%g", v); + } + } + fputc('\n', stderr); + } + } + else if (x->sem == SEM_REGEX) { + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, " handle=" PRINTF_P_PFX "%p\n", x->ring); + } + } +} + +void +__dumpMetric(int level, Metric *m) +{ + int i; + int j; + int numinst; + + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, "Metric dump @ " PRINTF_P_PFX "%p\n", m); + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, "expr=" PRINTF_P_PFX "%p profile=" PRINTF_P_PFX "%p host=" PRINTF_P_PFX "%p next=" PRINTF_P_PFX "%p prev=" PRINTF_P_PFX "%p\n", + m->expr, m->profile, m->host, m->next, m->prev); + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, "metric=%s host=%s conv=%g specinst=%d m_indom=%d\n", + symName(m->mname), symName(m->hname), m->conv, m->specinst, m->m_idom); + if (m->desc.indom != PM_INDOM_NULL) { + numinst = m->specinst == 0 ? m->m_idom : m->specinst; + for (j = 0; j < numinst; j++) { + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fprintf(stderr, "[%d] iid=", j); + if (m->iids[j] == PM_IN_NULL) + fprintf(stderr, "?missing"); + else + fprintf(stderr, "%d", m->iids[j]); + fprintf(stderr, " iname=\"%s\"\n", m->inames[j]); + } + } + for (i = 0; i < level; i++) fprintf(stderr, ".. "); + fputc('\n', stderr); + +#if 0 + pmDesc desc; /* pmAPI metric description */ + RealTime stamp; /* time stamp for current values */ + pmValueSet *vset; /* current values */ + RealTime stomp; /* previous time stamp for rate calculation */ + double *vals; /* vector of values for rate computation */ + int offset; /* offset within sample in expr ring buffer */ +... Metric; +#endif + +} + + +void +__dumpTree(int level, Expr *x) +{ + __dumpExpr(level, x); + if (x->arg1 != NULL) __dumpTree(level+1, x->arg1); + if (x->arg2 != NULL) __dumpTree(level+1, x->arg2); +} + +void +dumpTree(Expr *x) +{ + __dumpTree(0, x); +} + +void +dumpRules(void) +{ + Task *t; + Symbol *s; + int i; + + for (t = taskq; t != NULL; t = t->next) { + s = t->rules; + for (i = 0; i < t->nrules; i++, s++) { + fprintf(stderr, "\nRule: %s\n", symName(*s)); + dumpTree((Expr *)symValue(*s)); + } + } +} + +void +dumpExpr(Expr *x) +{ + __dumpExpr(0, x); +} + +void +dumpMetric(Metric *m) +{ + __dumpMetric(0, m); +} + +void +dumpTask(Task *t) +{ + int i; + fprintf(stderr, "Task dump @ " PRINTF_P_PFX "%p\n", t); + fprintf(stderr, " nth=%d delta=%.3f tick=%d next=" PRINTF_P_PFX "%p prev=" PRINTF_P_PFX "%p\n", t->nth, t->delta, t->tick, t->next, t->prev); + fprintf(stderr, " eval time: "); + showFullTime(stderr, t->eval); + fputc('\n', stderr); + fprintf(stderr, " retry time: "); + showFullTime(stderr, t->retry); + fputc('\n', stderr); + if (t->hosts == NULL) + fprintf(stderr, " host=<null>\n"); + else + fprintf(stderr, " host=%s (%s)\n", symName(t->hosts->name), t->hosts->down ? "down" : "up"); + fprintf(stderr, " rules:\n"); + for (i = 0; i < t->nrules; i++) { + fprintf(stderr, " %s\n", symName(t->rules[i])); + } +} diff --git a/src/pmie/src/dstruct.h b/src/pmie/src/dstruct.h new file mode 100644 index 0000000..919de35 --- /dev/null +++ b/src/pmie/src/dstruct.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * 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. + */ + +/*********************************************************************** + * dstruct.h - central data structures snd associated operations + ***********************************************************************/ + +#ifndef DSTRUCT_H +#define DSTRUCT_H + +#include <stddef.h> +#include <sys/types.h> +#include "pmapi.h" +#include "impl.h" +#include "symbol.h" +#include "stats.h" + + +/*********************************************************************** + * forward reference + ***********************************************************************/ + +struct expr; +struct metric; +struct profile; +struct fetch; +struct host; +struct task; + + +/*********************************************************************** + * (Kleene) 3-valued boolean values + ***********************************************************************/ + +typedef char Boolean; +#define B_FALSE 0 +#define B_TRUE 1 +#define B_UNKNOWN 2 + +extern double mynan; /* definitely not-a-number */ + + +/*********************************************************************** + * time + ***********************************************************************/ + +typedef unsigned int TickTime; /* time counted in deltas */ + +/* following in usec */ +typedef double RealTime; /* wall clock time or interval */ +#define MINUTE 60 /* one minute of real time */ +#define DELAY_MAX 32 /* maximum initial evaluation delay */ +#define RETRY 5 /* retry interval */ +#define DELTA_DFLT 10 /* default sample interval */ +#define DELTA_MIN 0.1 /* minimum sample interval */ + + +/*********************************************************************** + * evaluator functions + ***********************************************************************/ + +/* evaluator function */ +typedef void (Eval)(struct expr *); + + +/*********************************************************************** + * internal representation of rule expressions and their values + ***********************************************************************/ + +/* pointer to and timestamp for sample in ring buffer */ +typedef struct { + void *ptr; /* pointer into value ring buffer */ + RealTime stamp; /* timestamp for sample */ +} Sample; + +/* Expression Tree Node: + * The parser fills in most of the fields, pragmatics analysis + * fills in the rest. The parser may leave 0 in e_idom or nvals + * and NULL in ring to indicate it did not have enough information + * to fill in the correct values. These values are then patched + * up during pragmatics analysis and dynamically if/when the number + * of instances changes. + * + * Warning: The semantics of the hdom, e_idom and tdom fields are + * quite subtle. A value of 1 or greater is the cardinality of + * the corresponding domain. -1 indicates that the corresponding + * domain has collapsed, and there is one value. 0 indicates + * that there is no value (only used for e_idom to indicate an + * empty instance domain). + */ +typedef struct expr { + /* expression syntax */ + int op; /* operator */ + struct expr *arg1; /* NULL || (Expr *) */ + struct expr *arg2; /* NULL || (Expr *) */ + struct expr *parent; /* parent of this Expr */ + + /* evaluator */ + Eval *eval; /* evaluator function */ + int valid; /* number of valid samples */ + + /* description of value matrix */ + int hdom; /* cardinality of host dimension */ + int e_idom; /* cardinality of instance dimension */ + int tdom; /* cardinality of time dimension */ + int tspan; /* number of values per sample */ + int nsmpls; /* number of samples in ring buffer */ + int nvals; /* total number of values in ring buffer */ + struct metric *metrics; /* array of per host metric info */ + + /* description of single value */ + int sem; /* value semantics, see below */ + pmUnits units; /* value units, as in pmDesc */ + + /* value buffer */ + void *ring; /* base address of value ring buffer */ + Sample smpls[1]; /* array dynamically allocated */ +} Expr; + +/* per-host description of a performance metric */ +typedef struct metric { + struct expr *expr; /* Expr owning this Metric */ + struct profile *profile; /* Profile owning this Metric */ + struct host *host; /* Host owning this Metric */ + struct metric *next; /* fetch/wait list forward pointer */ + struct metric *prev; /* fetch/wait list backward pointer */ + Symbol mname; /* metric name */ + Symbol hname; /* host name */ + pmDesc desc; /* pmAPI metric description */ + double conv; /* conversion factor into canonical units */ + int specinst; /* count of specific instances in rule and */ + /* 0 if all instances are to be considered */ + int m_idom; /* cardinality of available instance domain */ + char **inames; /* array of instance names */ + int *iids; /* array of instance ids */ + RealTime stamp; /* time stamp for current values */ + pmValueSet *vset; /* current values */ + RealTime stomp; /* previous time stamp for rate calculation */ + double *vals; /* vector of values for rate computation */ + int offset; /* offset within sample in expr ring buffer */ +} Metric; + +/* + * Note on instances in Metric: + * + * if specinst == 0, then m_idom, inames[] and iids[] are the + * currently available instances + * otherwise, m_idom is the number of the specified instances currently + * available (and identified in inames[] and iids[] for 0 .. m_idom-1) + * and the unavailable instances are after that, i.e. elements + * m_idom ... specinst-1 of inames[] and iids[] + */ + +/* per instance-domain part of bundled fetch request */ +typedef struct profile { + struct metric *metrics; /* list of Metrics for this Profile */ + struct fetch *fetch; /* Fetch bundle owning this Profile */ + struct profile *next; /* Profile list forward link */ + struct profile *prev; /* Profile list backward link */ + pmInDom indom; /* instance domain */ + int need_all; /* all instances required */ +} Profile; + +/* bundled fetch request for multiple metrics */ +typedef struct fetch { + struct profile *profiles; /* list of Profiles for this Fetch */ + struct host *host; /* Host owning this Fetch */ + struct fetch *next; /* fetch list forward pointer */ + struct fetch *prev; /* fetch list backward pointer */ + int handle; /* PMCS context handle */ + int npmids; /* number of metrics in fetch */ + pmID *pmids; /* array of metric ids to fetch */ + pmResult *result; /* result of fetch */ +} Fetch; + +/* set of bundled fetches for single host (may be archive or live): + The field waits contains a list of Metrics for which descriptors + were not available during pragmatics analysis. */ +typedef struct host { + struct fetch *fetches; /* list of Fetches for this Host */ + struct task *task; /* Task owning this host */ + struct host *next; /* Host list forward pointer */ + struct host *prev; /* Host list backward pointer */ + Symbol name; /* host machine */ + int down; /* host is not delivering metrics */ + Metric *waits; /* wait list of Metrics */ + Metric *duds; /* bad Metrics discovered during evaluation */ +} Host; + +/* element of evaluator task queue */ +typedef struct task { + int nth; /* initial (syntactic) position in task queue */ + struct task *next; /* task list forward link */ + struct task *prev; /* task list backward link */ + RealTime epoch; /* bottom-line for timing calculations */ + RealTime delta; /* sample interval */ + TickTime tick; /* count up deltas */ + RealTime eval; /* scheduled evaluation time */ + RealTime retry; /* scheduled retry down Hosts and Metrics */ + int nrules; /* number of rules in this task */ + Symbol *rules; /* array of rules to be evaluated */ + Host *hosts; /* fetches to be executed and waiting */ + pmResult *rslt; /* for secret agent mode */ +} Task; + +/* value semantics - as in pmDesc plus following */ +#define SEM_UNKNOWN 0 /* semantics not yet available */ +#define SEM_NUMVAR 10 /* numeric variable value */ +#define SEM_BOOLEAN 11 /* boolean (3-state) value */ +#define SEM_CHAR 12 /* character (string) */ +#define SEM_NUMCONST 13 /* numeric constant value */ +#define SEM_REGEX 14 /* compiled regular expression */ + +/* Expr operator (op) tokens */ +typedef int Op; +#define RULE 0 +/* basic conditions */ +#define CND_FETCH 1 +#define CND_DELAY 2 +#define CND_RATE 3 +/* arithmetic */ +#define CND_NEG 4 +#define CND_ADD 5 +#define CND_SUB 6 +#define CND_MUL 7 +#define CND_DIV 8 +/* aggregation */ +#define CND_SUM_HOST 10 +#define CND_SUM_INST 11 +#define CND_SUM_TIME 12 +#define CND_AVG_HOST 13 +#define CND_AVG_INST 14 +#define CND_AVG_TIME 15 +#define CND_MAX_HOST 16 +#define CND_MAX_INST 17 +#define CND_MAX_TIME 18 +#define CND_MIN_HOST 19 +#define CND_MIN_INST 20 +#define CND_MIN_TIME 21 +/* relational */ +#define CND_EQ 30 +#define CND_NEQ 31 +#define CND_LT 32 +#define CND_LTE 33 +#define CND_GT 34 +#define CND_GTE 35 +/* boolean */ +#define CND_NOT 40 +#define CND_RISE 41 +#define CND_FALL 42 +#define CND_AND 43 +#define CND_OR 44 +#define CND_MATCH 45 +#define CND_NOMATCH 46 +#define CND_RULESET 47 +#define CND_OTHER 48 +/* quantification */ +#define CND_ALL_HOST 50 +#define CND_ALL_INST 51 +#define CND_ALL_TIME 52 +#define CND_SOME_HOST 53 +#define CND_SOME_INST 54 +#define CND_SOME_TIME 55 +#define CND_PCNT_HOST 56 +#define CND_PCNT_INST 57 +#define CND_PCNT_TIME 58 +#define CND_COUNT_HOST 59 +#define CND_COUNT_INST 60 +#define CND_COUNT_TIME 61 +/* actions */ +#define ACT_SEQ 70 +#define ACT_ALT 71 +#define ACT_SHELL 72 +#define ACT_ALARM 73 +#define ACT_SYSLOG 74 +#define ACT_PRINT 75 +#define ACT_ARG 76 +#define ACT_STOMP 77 +/* no operation (extension) */ +#define NOP 80 +/* dereferenced variable */ +#define OP_VAR 90 + +int unary(Op); /* unary operator */ +int binary(Op); /* binary operator */ + +/*********************************************************************** + * archives + ***********************************************************************/ + +typedef struct archive { + struct archive *next; /* list link */ + char *fname; /* file name */ + char *hname; /* host name */ + RealTime first; /* timestamp for first pmResult */ + RealTime last; /* timestamp for last pmResult */ +} Archive; + + +/*********************************************************************** + * memory allocation / deallocation + ***********************************************************************/ + +void *alloc(size_t); +void *zalloc(size_t); +void *ralloc(void *, size_t); +void *aalloc(size_t, size_t); +char *sdup(char *); + +Expr *newExpr(int, Expr *, Expr *, int, int, int, int, int); +Profile *newProfile(Fetch *, pmInDom); +Fetch *newFetch(Host *); +Host *newHost(Task *, Symbol); +Task *newTask(RealTime, int); +void newResult(Task *); + +void freeExpr(Expr *); + +void freeMetric(Metric *); + +void FreeProfile(Profile *); +void freeFetch(Fetch *); +void freeTask(Task *); + + +/*********************************************************************** + * ring buffer management + ***********************************************************************/ + +void newRingBfr(Expr *); +void newStringBfr(Expr *, size_t, char *); +void rotate(Expr *); + + +/*********************************************************************** + * Expr manipulation + ***********************************************************************/ + +Expr *primary(Expr *, Expr *); +void changeSmpls(Expr **, int); +void instFetchExpr(Expr *); + +/*********************************************************************** + * time methods + ***********************************************************************/ + +/* convert timeval to RealTime */ +#define realize(t) (1.0e-6 * (RealTime)(t).tv_usec + (RealTime)(t).tv_sec) +/* convert RealTime to timeval */ +void unrealize(RealTime, struct timeval *); +RealTime getReal(void); /* return current time */ +void reflectTime(RealTime); /* update time vars to reflect now */ +#define SLEEP_EVAL 0 +#define SLEEP_RETRY 1 +void sleepTight(Task *, int); /* sleep until retry or eval time */ +void logRotate(void); /* close current, start a new log */ + +/* + * diagnostic tracing + */ +void dumpRules(void); +void dumpExpr(Expr *); +void dumpTree(Expr *); +void dumpMetric(Metric *); +void dumpTask(Task *); +void __dumpExpr(int, Expr *); +void __dumpTree(int, Expr *); +void __dumpMetric(int, Metric *); + +/*********************************************************************** + * comparison functions (for use by qsort) + ***********************************************************************/ + +/* compare two instance identifiers. */ +int compid(const void *, const void *); + +/* compare two pmValue's on their inst fields */ +int compair(const void *, const void *); + + +/*********************************************************************** + * global data structures + ***********************************************************************/ + +extern char *pmnsfile; /* alternate namespace */ +extern Archive *archives; /* archives given on command line */ +extern RealTime first; /* archive starting point */ +extern RealTime last; /* archive end point */ +extern char *dfltHostConn; /* default PM_CONTEXT_HOST parameter */ +extern char *dfltHostName; /* pmContextGetHostName of host name */ +extern RealTime dfltDelta; /* default sample interval */ +extern RealTime runTime; /* run time interval */ +extern int hostZone; /* timezone from host? */ +extern char *timeZone; /* timezone from command line */ +extern int verbose; /* verbosity 0, 1 or 2 */ +extern int interactive; /* interactive mode, -d */ +extern int isdaemon; /* run as a daemon */ +extern int agent; /* secret agent mode? */ +extern int applet; /* applet mode? */ +extern int dowrap; /* counter wrap? default no */ +extern int doexit; /* signalled its time to exit */ +extern int dorotate; /* log rotation was requested */ +extern pmiestats_t *perf; /* pmie performance data ptr */ +extern pmiestats_t instrument; /* pmie performance data struct */ + + +extern SymbolTable rules; /* currently known rules */ +extern SymbolTable vars; /* currently known variables */ +extern SymbolTable hosts; /* currently known hosts */ +extern SymbolTable metrics; /* currently known metrics */ + +extern Task *taskq; /* evaluator task queue */ +extern Expr *curr; /* current executing rule expression */ + +extern RealTime now; /* current time */ +extern RealTime start; /* start evaluation */ +extern RealTime stop; /* stop evaluation */ + + +/*********************************************************************** + * reserved symbols + ***********************************************************************/ + +extern Symbol symDelta; /* current sample interval */ +extern Symbol symMinute; /* minutes after the hour 0..59 */ +extern Symbol symHour; /* hours since midnight 0..23 */ +extern Symbol symDay; /* day of the month 1..31 */ +extern Symbol symMonth; /* month of the year 1..12 */ +extern Symbol symYear; /* year 1996.. */ +extern Symbol symWeekday; /* days since Sunday 0..6 */ + + +/*********************************************************************** + * compulsory initialization + ***********************************************************************/ + +void dstructInit(void); /* initialize central data structures */ +void timeInit(void); /* initialize time keeping data structures */ +void agentInit(void); /* initialize evaluation parameters */ + +/*********************************************************************** + * unknown units handling + * We don't have a good sentinal value, but 1 / count x 10 ^ 7 does + * not appear in any PMDA and cannot come from the pmie lexical scanner + ***********************************************************************/ +#define SET_UNITS_UNKNOWN(u) { u.dimCount = -1; u.scaleCount = 7; } +#define UNITS_UNKNOWN(u) (u.dimCount == -1 && u.scaleCount == 7) + +#endif /* DSTRUCT_H */ diff --git a/src/pmie/src/eval.c b/src/pmie/src/eval.c new file mode 100644 index 0000000..e119868 --- /dev/null +++ b/src/pmie/src/eval.c @@ -0,0 +1,808 @@ +/*********************************************************************** + * eval.c - task scheduling and expression evaluation + *********************************************************************** + * + * 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. + * + * 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 <limits.h> +#include "dstruct.h" +#include "eval.h" +#include "fun.h" +#include "pragmatics.h" +#include "show.h" + +/*********************************************************************** + * scheduling + ***********************************************************************/ + +/* enter Task into task queue */ +static void +enque(Task *t) +{ + Task *q; + RealTime tt; + RealTime qt; + + q = taskq; + if (q == NULL) { + taskq = t; + t->next = NULL; + t->prev = NULL; + } + else { + tt = (t->retry && t->retry < t->eval) ? t->retry : t->eval; + while (q) { + qt = (q->retry && q->retry < q->eval) ? q->retry : q->eval; + if (tt <= qt) { + t->next = q; + t->prev = q->prev; + if (q->prev) q->prev->next = t; + else taskq = t; + q->prev = t; + break; + } + if (q->next == NULL) { + q->next = t; + t->next = NULL; + t->prev = q; + break; + } + q = q->next; + } + } +} + + +/*********************************************************************** + * reconnect + ***********************************************************************/ + +/* any hosts down or unavailable metrics in this task? */ +static int +waiting(Task *t) +{ + Host *h; + + h = t->hosts; + while (h) { + if (h->down || h->waits) + return 1; + h = h->next; + } + return 0; +} + +/* + * state values + * STATE_INIT + * STATE_FAILINIT + * STATE_RECONN + * STATE_LOSTCONN + */ + +typedef struct hstate { + struct hstate *next; + char *name; + int state; +} hstate_t; + +static hstate_t *host_map = NULL; + +int +host_state_changed(char *host, int state) +{ + hstate_t *hsp; + + for (hsp = host_map; hsp != NULL; hsp = hsp->next) { + if (strcmp(host, hsp->name) == 0) + break; + } + + if (hsp == NULL) { + hsp = (hstate_t *)alloc(sizeof(*hsp)); + hsp->next = host_map; + hsp->name = sdup(host); + hsp->state = STATE_INIT; + host_map = hsp; + } + + if (state == hsp->state) return 0; + + if (state == STATE_FAILINIT) + __pmNotifyErr(LOG_INFO, "Cannot connect to pmcd on host %s\n", host); + else if (state == STATE_RECONN && hsp->state != STATE_INIT) + __pmNotifyErr(LOG_INFO, "Re-established connection to pmcd on host %s\n", host); + else if (state == STATE_LOSTCONN) + __pmNotifyErr(LOG_INFO, "Lost connection to pmcd on host %s\n", host); + + hsp->state = state; + return 1; +} + +/* try to reconnect to hosts and initialize missing metrics */ +static void +enable(Task *t) +{ + Host *h; + Metric *m; + Metric **p; + + h = t->hosts; + while (h) { + + /* reconnect to host */ + if (h->down) { + if (reconnect(h)) { + h->down = 0; + host_state_changed(symName(h->name), STATE_RECONN); + } + } + + /* reinitialize waiting Metrics */ + if ((! h->down) && (h->waits)) { + p = &h->waits; + m = *p; + while (m) { + switch (reinitMetric(m)) { + case 1: + *p = m->next; + unwaitMetric(m); + bundleMetric(h,m); + break; + case 0: + p = &m->next; + break; + case -1: + *p = m->next; + m->next = h->duds; + h->duds = m; + break; + } + m = *p; + } + } + + h = h->next; + } +} + + +/*********************************************************************** + * evaluation + ***********************************************************************/ + +int showTimeFlag = 0; /* set when -e used on the command line */ + +/* evaluate Task */ +static void +eval(Task *task) +{ + Symbol *s; + pmValueSet *vset; + int i; + + /* fetch metrics */ + taskFetch(task); + + /* evaluate rule expressions */ + s = task->rules; + for (i = 0; i < task->nrules; i++) { + curr = symValue(*s); + if (curr->op < NOP) { + (curr->eval)(curr); + perf->eval_actual++; + } + s++; + } + + if (verbose) { + + /* send binary values */ + if (agent) { + int sts; + s = task->rules; + for (i = 0; i < task->nrules; i++) { + vset = task->rslt->vset[i]; + fillVSet(symValue(*s), vset); + s++; + } + __pmOverrideLastFd(PDU_OVERRIDE2); + sts = __pmSendResult(STDOUT_FILENO, pmWhichContext(), task->rslt); + if (sts < 0) { + fprintf(stderr, "Error: __pmSendResult to summary agent failed: %s\n", pmErrStr(sts)); + exit(0); + } + + } + + /* send values to applet */ + else if (applet) { + s = task->rules; + for (i = 0; i < task->nrules; i++) { + showValue(stdout, symValue(*s)); + putchar(' '); + s++; + } + putchar('\n'); + } + + /* print values in ASCII */ + else { + s = task->rules; + for (i = 0; i < task->nrules; i++) { + printf("%s", symName(*s)); + if (archives || showTimeFlag) { + printf(" ("); + showTime(stdout, now); + putchar(')'); + } + printf(": "); + switch (verbose) { + case 1: + showValue(stdout, symValue(*s)); + break; + case 2: + showAnnotatedValue(stdout, symValue(*s)); + break; + case 3: + showSatisfyingValue(stdout, symValue(*s)); + break; + } + putchar('\n'); + s++; + } + putchar('\n'); + } + } +} + + +/* Mark expression as having invalid values */ +void +clobber(Expr *x) +{ + int i; + Boolean *t; + double *d; + + if (x->op < NOP) { + if (x->arg1) + clobber(x->arg1); + if (x->arg2) + clobber(x->arg2); + x->valid = 0; + /* + * numeric variable or variable? + */ + if (x->sem == PM_SEM_COUNTER || + x->sem == PM_SEM_INSTANT || x->sem == PM_SEM_DISCRETE || + x->sem == SEM_NUMVAR) { + d = (double *) x->ring; + for (i = 0; i < x->nvals; i++) + *d++ = mynan; + } + else if (x->sem == SEM_BOOLEAN) { + t = (Boolean *) x->ring; + for (i = 0; i < x->nvals; i++) + *t++ = B_UNKNOWN; + } + } +} + + +/*********************************************************************** + * exported functions + ***********************************************************************/ + +/* fill in appropriate evaluator function for given Expr */ +void +findEval(Expr *x) +{ + int arity = 0; + Metric *m; + + /* + * arity values constructed from bit masks + * 1 arg1 has tspan 1, and must always have one metric value + * 2 arg2 has tspan 1, and must always have one metric value + */ + if (x->arg1 && x->arg1->tspan == 1) { + for (m = x->arg1->metrics; m; m = m->next) { + if (m->desc.indom == PM_INDOM_NULL) continue; + if (m->specinst == 0) break; + } + if (m == NULL) arity |= 1; + } + if (x->arg2 && x->arg2->tspan == 1) { + for (m = x->arg2->metrics; m; m = m->next) { + if (m->desc.indom == PM_INDOM_NULL) continue; + if (m->specinst == 0) break; + } + if (m == NULL) arity |= 2; + } + + /* + * never come here with x->op == NULL or OP_VAR + */ + switch (x->op) { + + case RULE: + x->eval = rule; + break; + + case CND_RULESET: + x->eval = ruleset; + break; + + case CND_FETCH: + if (x->metrics->desc.indom == PM_INDOM_NULL || + x->metrics->conv == 0) + x->eval = cndFetch_1; + else if (x->metrics->specinst == 0) + x->eval = cndFetch_all; + else + x->eval = cndFetch_n; + break; + + case CND_SUM_HOST: + x->eval = cndSum_host; + break; + + case CND_SUM_INST: + x->eval = cndSum_inst; + break; + + case CND_SUM_TIME: + x->eval = cndSum_time; + break; + + case CND_AVG_HOST: + x->eval = cndAvg_host; + break; + + case CND_AVG_INST: + x->eval = cndAvg_inst; + break; + + case CND_AVG_TIME: + x->eval = cndAvg_time; + break; + + case CND_MAX_HOST: + x->eval = cndMax_host; + break; + + case CND_MAX_INST: + x->eval = cndMax_inst; + break; + + case CND_MAX_TIME: + x->eval = cndMax_time; + break; + + case CND_MIN_HOST: + x->eval = cndMin_host; + break; + + case CND_MIN_INST: + x->eval = cndMin_inst; + break; + + case CND_MIN_TIME: + x->eval = cndMin_time; + break; + + case CND_ALL_HOST: + x->eval = cndAll_host; + break; + + case CND_ALL_INST: + x->eval = cndAll_inst; + break; + + case CND_ALL_TIME: + x->eval = cndAll_time; + break; + + case CND_SOME_HOST: + x->eval = cndSome_host; + break; + + case CND_SOME_INST: + x->eval = cndSome_inst; + break; + + case CND_SOME_TIME: + x->eval = cndSome_time; + break; + + case CND_PCNT_HOST: + x->eval = cndPcnt_host; + break; + + case CND_PCNT_INST: + x->eval = cndPcnt_inst; + break; + + case CND_PCNT_TIME: + x->eval = cndPcnt_time; + break; + + case CND_COUNT_HOST: + x->eval = cndCount_host; + break; + + case CND_COUNT_INST: + x->eval = cndCount_inst; + break; + + case CND_COUNT_TIME: + x->eval = cndCount_time; + break; + + case ACT_SEQ: + x->eval = actAnd; + break; + + case ACT_ALT: + x->eval = actOr; + break; + + case ACT_SHELL: + x->eval = actShell; + break; + + case ACT_ALARM: + x->eval = actAlarm; + break; + + case ACT_STOMP: + x->eval = actStomp; + break; + + case ACT_SYSLOG: + x->eval = actSyslog; + break; + + case ACT_PRINT: + x->eval = actPrint; + break; + + case ACT_ARG: + x->eval = actArg; + break; + + case CND_DELAY: + if (arity & 1) + x->eval = cndDelay_1; + else + x->eval = cndDelay_n; + break; + + case CND_RATE: + if (arity & 1) + x->eval = cndRate_1; + else + x->eval = cndRate_n; + break; + + case CND_NEG: + if (arity & 1) + x->eval = cndNeg_1; + else + x->eval = cndNeg_n; + break; + + case CND_NOT: + if (arity & 1) + x->eval = cndNot_1; + else + x->eval = cndNot_n; + break; + + case CND_RISE: + if (arity & 1) + x->eval = cndRise_1; + else + x->eval = cndRise_n; + break; + + case CND_FALL: + if (arity & 1) + x->eval = cndFall_1; + else + x->eval = cndFall_n; + break; + + case CND_ADD: + if (arity & 1) { + if (arity & 2) + x->eval = cndAdd_1_1; + else + x->eval = cndAdd_1_n; + } + else { + if (arity & 2) + x->eval = cndAdd_n_1; + else + x->eval = cndAdd_n_n; + } + break; + + case CND_SUB: + if (arity & 1) { + if (arity & 2) + x->eval = cndSub_1_1; + else + x->eval = cndSub_1_n; + } + else { + if (arity & 2) + x->eval = cndSub_n_1; + else + x->eval = cndSub_n_n; + } + break; + + case CND_MUL: + if (arity & 1) { + if (arity & 2) + x->eval = cndMul_1_1; + else + x->eval = cndMul_1_n; + } + else { + if (arity & 2) + x->eval = cndMul_n_1; + else + x->eval = cndMul_n_n; + } + break; + + case CND_DIV: + if (arity & 1) { + if (arity & 2) + x->eval = cndDiv_1_1; + else + x->eval = cndDiv_1_n; + } + else { + if (arity & 2) + x->eval = cndDiv_n_1; + else + x->eval = cndDiv_n_n; + } + break; + + case CND_EQ: + if (arity & 1) { + if (arity & 2) + x->eval = cndEq_1_1; + else + x->eval = cndEq_1_n; + } + else { + if (arity & 2) + x->eval = cndEq_n_1; + else + x->eval = cndEq_n_n; + } + break; + + case CND_NEQ: + if (arity & 1) { + if (arity & 2) + x->eval = cndNeq_1_1; + else + x->eval = cndNeq_1_n; + } + else { + if (arity & 2) + x->eval = cndNeq_n_1; + else + x->eval = cndNeq_n_n; + } + break; + + case CND_LT: + if (arity & 1) { + if (arity & 2) + x->eval = cndLt_1_1; + else + x->eval = cndLt_1_n; + } + else { + if (arity & 2) + x->eval = cndLt_n_1; + else + x->eval = cndLt_n_n; + } + break; + + case CND_LTE: + if (arity & 1) { + if (arity & 2) + x->eval = cndLte_1_1; + else + x->eval = cndLte_1_n; + } + else { + if (arity & 2) + x->eval = cndLte_n_1; + else + x->eval = cndLte_n_n; + } + break; + + case CND_GT: + if (arity & 1) { + if (arity & 2) + x->eval = cndGt_1_1; + else + x->eval = cndGt_1_n; + } + else { + if (arity & 2) + x->eval = cndGt_n_1; + else + x->eval = cndGt_n_n; + } + break; + + case CND_GTE: + if (arity & 1) { + if (arity & 2) + x->eval = cndGte_1_1; + else + x->eval = cndGte_1_n; + } + else { + if (arity & 2) + x->eval = cndGte_n_1; + else + x->eval = cndGte_n_n; + } + break; + + case CND_AND: + if (arity & 1) { + if (arity & 2) + x->eval = cndAnd_1_1; + else + x->eval = cndAnd_1_n; + } + else { + if (arity & 2) + x->eval = cndAnd_n_1; + else + x->eval = cndAnd_n_n; + } + break; + + case CND_OR: + if (arity & 1) { + if (arity & 2) + x->eval = cndOr_1_1; + else + x->eval = cndOr_1_n; + } + else { + if (arity & 2) + x->eval = cndOr_n_1; + else + x->eval = cndOr_n_n; + } + break; + + case CND_MATCH: + case CND_NOMATCH: + x->eval = cndMatch_inst; + break; + + case CND_OTHER: + /* OTHER is not really evaluated in this sense, see ruleset() */ + x->eval = NULL; + break; + + default: + __pmNotifyErr(LOG_ERR, "findEval: internal error: bad op (%d) %s\n", x->op, opStrings(x->op)); + dumpExpr(x); + exit(1); + } + + /* patch in fake actions for archive mode */ + if (archives && + (x->op == ACT_SHELL || x->op == ACT_ALARM || x->op == ACT_SYSLOG || + x->op == ACT_PRINT || x->op == ACT_STOMP)) { + x->eval = actFake; + } +} + + +/* run evaluator */ +void +run(void) +{ + Task *t; + + /* empty task queue */ + if (taskq == NULL) + return; + + /* initialize task scheduling */ + t = taskq; + while (t) { + t->eval = t->epoch = start; + t->retry = 0; + t->tick = 0; + t = t->next; + } + + /* evaluate and reschedule */ + t = taskq; + for (;;) { + if (t->retry && t->retry < t->eval) { + now = t->retry; + if (now > stop) + break; + sleepTight(t, SLEEP_RETRY); + enable(t); + t->retry = waiting(t) ? now + RETRY : 0; + } + else { + now = t->eval; + if (now > stop) + break; + reflectTime(t->delta); + sleepTight(t, SLEEP_EVAL); + eval(t); + t->tick++; + t->eval = t->epoch + t->tick * t->delta; + if ((! t->retry) && waiting(t)) + t->retry = now + RETRY; + } + taskq = t->next; + if (taskq) taskq->prev = NULL; + enque(t); + t = taskq; + } + __pmNotifyErr(LOG_INFO, "evaluator exiting\n"); +} + + +/* invalidate all expressions being evaluated + i.e. mark values as unknown */ +void +invalidate(void) +{ + Task *t; + Expr *x; + Symbol *s; + int i; + + t = taskq; + while (t) { + s = t->rules; + for (i = 0; i < t->nrules; i++) { + x = symValue(*s); + clobber(x); + s++; + } + t = t->next; + } +} diff --git a/src/pmie/src/eval.h b/src/pmie/src/eval.h new file mode 100644 index 0000000..0333725 --- /dev/null +++ b/src/pmie/src/eval.h @@ -0,0 +1,46 @@ +/*********************************************************************** + * eval.h + *********************************************************************** + * + * 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 EVAL_H +#define EVAL_H + +#include "dstruct.h" + +/* fill in apprpriate evaluator function for given Expr */ +void findEval(Expr *); + +/* run evaluator until specified time reached */ +void run(void); + +/* invalidate one expression (and descendents) */ +void clobber(Expr *); + +/* invalidate all expressions being evaluated */ +void invalidate(void); + +/* report changes in pmcd connection state */ +#define STATE_INIT 0 +#define STATE_FAILINIT 1 +#define STATE_RECONN 2 +#define STATE_LOSTCONN 3 +int host_state_changed(char *, int); + +#endif /* EVAL_H */ + diff --git a/src/pmie/src/fetch.sk b/src/pmie/src/fetch.sk new file mode 100644 index 0000000..76bcb74 --- /dev/null +++ b/src/pmie/src/fetch.sk @@ -0,0 +1,460 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * skeleton: fetch.sk - fetch metric values + ***********************************************************************/ + +/* + * operator: cndFetch + */ + +static int +indom_changed(Metric *m) +{ + int changed = 0; + int j; + + /* check for changes in the instance domain */ + if (m->vset == NULL || m->vset->numval <= 0) { + if (m->m_idom > 0) + changed = 1; + } + else { + if (m->vset->numval != m->m_idom) + changed = 1; + else { + for (j = 0; j < m->m_idom; j++) { + if (m->iids[j] != m->vset->vlist[j].inst) { + changed = 1; + break; + } + } + } + } + + if (changed) { + int old; + int new; + int numval; + char **inames; + int *iids; + int sts; + int handle = -1; + int old_handle = -1; + char **get_inames; + int *get_iids; + int numinst = -1; + + if (m->vset == NULL || m->vset->numval <= 0) + numval = 0; + else + numval = m->vset->numval; + + /* build new inames[] and iids[] */ + if (numval > 0) { + inames = (char **)alloc(numval * sizeof(char *)); + iids = (int *)alloc(numval * sizeof(int)); + if (numval > m->m_idom && m->desc.sem == PM_SEM_COUNTER) { + /* more instances, so expand the val array */ + m->vals = (double *)ralloc(m->vals, numval * sizeof(double)); + for (j = m->m_idom; j < numval; j++) + m->vals[j] = 0; + } + } + else { + inames = NULL; + iids = NULL; + } + + for (new = 0; new < numval; new++) { + for (old = 0; old < m->m_idom; old++) { + if (m->iids[old] == m->vset->vlist[new].inst) + break; + } + if (old < m->m_idom) { + /* in both lists */ + inames[new] = m->inames[old]; + m->inames[old] = NULL; + iids[new] = m->iids[old]; + if (m->desc.sem == PM_SEM_COUNTER && new != old) { + /* swap vals[] */ + double d; + d = m->vals[new]; + m->vals[new] = m->vals[old]; + m->vals[old] = d; + } + } + else { + /* new one */ + inames[new] = NULL; + iids[new] = m->vset->vlist[new].inst; + } + } + + /* + * clean up old inames and iids, the install new ones + */ + if (m->m_idom > 0 && m->inames != NULL) { + for (old = 0; old < m->m_idom; old++) { + if (m->inames[old] != NULL) free(m->inames[old]); + } + } + if (m->inames != NULL) + free(m->inames); + if (m->iids != NULL) + free(m->iids); + m->inames = inames; + m->iids = iids; + m->m_idom = numval; + + for (new = 0; new < m->m_idom; new++) { + if (m->inames[new] == NULL) { + if (handle < 0) { + /* set up temporary context */ + if (old_handle < 0) + old_handle = pmWhichContext(); + handle = newContext(symName(m->hname)); + } + if (handle < 0) { + sts = -1; + } + else { + if (archives) { + if ((sts = pmNameInDomArchive(m->desc.indom, m->iids[new], &m->inames[new])) < 0) { + __pmNotifyErr(LOG_ERR, "metric %s from %s: instance domain not " + "available in archive\npmNameInDomArchive failed: %s\n", + symName(m->mname), findsource(symName(m->hname)), pmErrStr(sts)); + } + } + else { + if (numinst == -1) { + if ((sts = pmGetInDom(m->desc.indom, &get_iids, &get_inames)) < 0) { + __pmNotifyErr(LOG_ERR, "metric %s from %s: instance domain not (currently) available\n" + "pmGetInDom failed: %s\n", + symName(m->mname), findsource(symName(m->hname)), pmErrStr(sts)); + } + else + numinst = sts; + } + sts = -1; + for (j = 0; j < numinst; j++) { + if (m->iids[new] == get_iids[j]) { + m->inames[new] = sdup(get_inames[j]); + sts = 0; + break; + } + } + } + } + if (sts < 0) { + /* ugly, but not much choice */ + m->inames[new] = sdup("inst#xxxxxxxxxxxx?"); + sprintf(m->inames[new], "inst#%d?", m->iids[new]); + } + + if (m->desc.sem == PM_SEM_COUNTER) + m->vals[new] = 0; + } + } + if (handle >= 0) { + pmDestroyContext(handle); + if (old_handle >= 0) + pmUseContext(old_handle); + } + if (numinst > 0) { + /* pmGetInDom returned some instances above */ + free(get_iids); + free(get_inames); + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "indom_changed: %s from %s\n", + symName(m->mname), symName(m->hname)); + if (m->m_idom < 1) fprintf(stderr, " %d instances!\n", m->m_idom); + for (j = 0; j < m->m_idom; j++) { + fprintf(stderr, " indom[%d] %d \"%s\"\n", + j, m->iids[j], m->inames[j]); + } + } +#endif + } + + return changed; +} + +/* null instance domain - so 1 instance only */ +void +cndFetch_1(Expr *x) +{ + Metric *m = x->metrics; + double *op; + RealTime stamp = 0; + pmAtomValue a; + double t; + int i, j; + + ROTATE(x) + x->valid++; + op = (double *)x->smpls[0].ptr; + + for (i = 0; i < x->hdom; i++) { + /* extract value */ + if (m->vset && m->vset->numval == 1) { + pmExtractValue(m->vset->valfmt, &m->vset->vlist[0], m->desc.type, &a, PM_TYPE_DOUBLE); + *op++ = m->conv * a.d; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndFetch_1: %s from %s = %g", + symName(m->mname), symName(m->hname), m->conv * a.d); + if (m->conv != 1) fprintf(stderr, " (unconv = %g)", a.d); + fputc('\n', stderr); + } +#endif + } + + /* no value */ + else { + x->valid = 0; + for (j = i; j < x->hdom; j++) { + m->stomp = 0; + m->vset = NULL; + m++; + } + return; + } + m->vset = NULL; + + /* rate computation */ + if (m->desc.sem == PM_SEM_COUNTER) { + op--; + if (m->m_idom > 0) { + t = *op - m->vals[0]; + if (t < 0.0 && dowrap) { + switch (m->desc.type) { + case PM_TYPE_32: + case PM_TYPE_U32: + t += (double)UINT_MAX+1; + break; + case PM_TYPE_64: + case PM_TYPE_U64: + t += (double)ULONGLONG_MAX+1; + break; + } + } + t /= (m->stamp - m->stomp); + m->vals[0] = *op; + if (t < 0.0) x->valid = 0; + else *op = t; + op++; + } + if (m->stomp == 0) x->valid = 0; + m->stomp = m->stamp; + } + + /* pick up most recent timestamp */ + if (m->stamp > stamp) stamp = m->stamp; + + m++; + } + x->smpls[0].stamp = stamp; +} + +void +cndFetch_all(Expr *x) +{ + Metric *m = x->metrics; + double *op; + RealTime stamp = 0; + pmAtomValue a; + double t; + int fix_idom = 0; + int i, j; + + ROTATE(x) + + /* preliminary scan through Metrics */ + for (i = 0; i < x->hdom; i++) { + /* check for different instances */ + if (indom_changed(m)) { + fix_idom = 1; + m->stomp = 0; + } + m++; + } + + if (fix_idom) { + /* + * propagate indom changes up the expression tree + * and reshape the ring buffer if required + */ + instFetchExpr(x); + } + + /* + * even if ring buffer reshaped in instFetchExpr(), we're + * about to populate it with another set of values, so bump + * valid counter + */ + x->valid++; + + /* extract values */ + op = (double *)x->smpls[0].ptr; + m = x->metrics; + for (i = 0; i < x->hdom; i++) { + + /* extract values from m->vset */ + for (j = 0; j < m->m_idom; j++) { + pmExtractValue(m->vset->valfmt, &m->vset->vlist[j], m->desc.type, &a, PM_TYPE_DOUBLE); + *op++ = m->conv * a.d; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndFetch_all: %s[%s] from %s = %g", + symName(m->mname), m->inames[j], symName(m->hname), m->conv * a.d); + if (m->conv != 1) fprintf(stderr, " (unconv = %g)", a.d); + fputc('\n', stderr); + } +#endif + } + m->vset = NULL; + + /* rate computation */ + if (m->desc.sem == PM_SEM_COUNTER) { + op -= m->m_idom; + for (j = 0; j < m->m_idom; j++) { + t = *op - m->vals[j]; + if (t < 0.0 && dowrap) { + switch (m->desc.type) { + case PM_TYPE_32: + case PM_TYPE_U32: + t += (double)UINT_MAX+1; + break; + case PM_TYPE_64: + case PM_TYPE_U64: + t += (double)ULONGLONG_MAX+1; + break; + } + } + t /= (m->stamp - m->stomp); + m->vals[j] = *op; + if (t < 0.0) x->valid = 0; + else *op = t; + op++; + } + if (m->stomp == 0) x->valid = 0; + m->stomp = m->stamp; + } + + /* pick up most recent timestamp */ + if (m->stamp > stamp) stamp = m->stamp; + + m++; + } + x->smpls[0].stamp = stamp; +} + +void +cndFetch_n(Expr *x) +{ + Metric *m = x->metrics; + double *op; + RealTime stamp = 0; + pmAtomValue a; + double t; + int i, j, k; + + ROTATE(x) + x->valid++; + op = (double *)x->smpls[0].ptr; + + for (i = 0; i < x->hdom; i++) { + /* no values */ + if ((m->vset == NULL) || (m->vset->numval < 0)) { + x->valid = 0; + for (j = i; j < x->hdom; j++) { + m->stomp = 0; + m->vset = NULL; + m++; + } + return; + } + + /* extract values */ + for (j = 0; j < m->m_idom; j++) { + for (k = 0; k < m->vset->numval; k++) { + if (m->iids[j] == m->vset->vlist[k].inst) { + pmExtractValue(m->vset->valfmt, &m->vset->vlist[k], m->desc.type, &a, PM_TYPE_DOUBLE); + *op++ = m->conv * a.d; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndFetch_n: %s[%s] from %s = %g", + symName(m->mname), m->inames[j], symName(m->hname), m->conv * a.d); + if (m->conv != 1) fprintf(stderr, " (unconv = %g)", a.d); + fputc('\n', stderr); + } +#endif + break; + } + } + + /* missing value */ + if (k == m->vset->numval) { + x->valid = 0; + m->stomp = 0; + m->vset = NULL; + return; + } + } + m->vset = NULL; + + /* rate computation */ + if (m->desc.sem == PM_SEM_COUNTER) { + op -= m->m_idom; + for (j = 0; j < m->m_idom; j++) { + t = *op - m->vals[j]; + if (t < 0.0 && dowrap) { + switch (m->desc.type) { + case PM_TYPE_32: + case PM_TYPE_U32: + t += (double)UINT_MAX+1; + break; + case PM_TYPE_64: + case PM_TYPE_U64: + t += (double)ULONGLONG_MAX+1; + break; + } + } + t /= (m->stamp - m->stomp); + m->vals[j] = *op; + if (t < 0.0) x->valid = 0; + else *op = t; + op++; + } + if (m->stomp == 0) x->valid = 0; + m->stomp = m->stamp; + } + + /* pick up most recent timestamp */ + if (m->stamp > stamp) stamp = m->stamp; + + m++; + } + x->smpls[0].stamp = stamp; +} + + diff --git a/src/pmie/src/fun.h b/src/pmie/src/fun.h new file mode 100644 index 0000000..532b4af --- /dev/null +++ b/src/pmie/src/fun.h @@ -0,0 +1,124 @@ +/*********************************************************************** + * fun.h - expression evaluator functions + *********************************************************************** + * + * 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 FUN_H +#define FUN_H + +#include "dstruct.h" +#include "andor.h" + +#define ROTATE(x) if ((x)->nsmpls > 1) rotate(x); +#define EVALARG(x) if ((x)->op < NOP) ((x)->eval)(x); + +/* expression evaluator function prototypes */ +void rule(Expr *); +void ruleset(Expr *); +void cndFetch_all(Expr *); +void cndFetch_n(Expr *); +void cndFetch_1(Expr *); +void cndDelay_n(Expr *); +void cndDelay_1(Expr *); +void cndRate_n(Expr *); +void cndRate_1(Expr *); +void cndSum_host(Expr *); +void cndSum_inst(Expr *); +void cndSum_time(Expr *); +void cndAvg_host(Expr *); +void cndAvg_inst(Expr *); +void cndAvg_time(Expr *); +void cndMax_host(Expr *); +void cndMax_inst(Expr *); +void cndMax_time(Expr *); +void cndMin_host(Expr *); +void cndMin_inst(Expr *); +void cndMin_time(Expr *); +void cndNeg_n(Expr *); +void cndNeg_1(Expr *); +void cndAdd_n_n(Expr *); +void cndAdd_1_n(Expr *); +void cndAdd_n_1(Expr *); +void cndAdd_1_1(Expr *); +void cndSub_n_n(Expr *); +void cndSub_1_n(Expr *); +void cndSub_n_1(Expr *); +void cndSub_1_1(Expr *); +void cndMul_n_n(Expr *); +void cndMul_1_n(Expr *); +void cndMul_n_1(Expr *); +void cndMul_1_1(Expr *); +void cndDiv_n_n(Expr *); +void cndDiv_1_n(Expr *); +void cndDiv_n_1(Expr *); +void cndDiv_1_1(Expr *); +void cndEq_n_n(Expr *); +void cndEq_1_n(Expr *); +void cndEq_n_1(Expr *); +void cndEq_1_1(Expr *); +void cndNeq_n_n(Expr *); +void cndNeq_1_n(Expr *); +void cndNeq_n_1(Expr *); +void cndNeq_1_1(Expr *); +void cndLt_n_n(Expr *); +void cndLt_1_n(Expr *); +void cndLt_n_1(Expr *); +void cndLt_1_1(Expr *); +void cndLte_n_n(Expr *); +void cndLte_1_n(Expr *); +void cndLte_n_1(Expr *); +void cndLte_1_1(Expr *); +void cndGt_n_n(Expr *); +void cndGt_1_n(Expr *); +void cndGt_n_1(Expr *); +void cndGt_1_1(Expr *); +void cndGte_n_n(Expr *); +void cndGte_1_n(Expr *); +void cndGte_n_1(Expr *); +void cndGte_1_1(Expr *); +void cndNot_n(Expr *); +void cndNot_1(Expr *); +void cndRise_n(Expr *); +void cndRise_1(Expr *); +void cndFall_n(Expr *); +void cndFall_1(Expr *); +void cndMatch_inst(Expr *); +void cndAll_host(Expr *); +void cndAll_inst(Expr *); +void cndAll_time(Expr *); +void cndSome_host(Expr *); +void cndSome_inst(Expr *); +void cndSome_time(Expr *); +void cndPcnt_host(Expr *); +void cndPcnt_inst(Expr *); +void cndPcnt_time(Expr *); +void cndCount_host(Expr *); +void cndCount_inst(Expr *); +void cndCount_time(Expr *); +void actAnd(Expr *); +void actOr(Expr *); +void actShell(Expr *); +void actAlarm(Expr *); +void actSyslog(Expr *); +void actPrint(Expr *); +void actStomp(Expr *); +void actArg(Expr *); +void actFake(Expr *); + +#endif /* FUN_H */ + diff --git a/src/pmie/src/grammar.y b/src/pmie/src/grammar.y new file mode 100644 index 0000000..0760488 --- /dev/null +++ b/src/pmie/src/grammar.y @@ -0,0 +1,666 @@ +/*********************************************************************** + * grammar.y - yacc grammar for rule language + *********************************************************************** + * + * 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 "dstruct.h" +#include "syntax.h" +#include "lexicon.h" +#include "pragmatics.h" +#include "systemlog.h" +#include "stomp.h" +#include "show.h" + +/* strings for error reporting */ +char precede[] = "precede"; +char follow[] = "follow"; +char act_str[] = "action"; +char bexp_str[] = "logical expression"; +char aexp_str[] = "arithmetic expression"; +char quant_str[] = "quantifier"; +char aggr_str[] = "aggregation operator"; +char pcnt_str[] = "percentage quantifier"; +char host_str[] = "host name"; +char inst_str[] = "instance name"; +char sample_str[] = "sample number(s)"; +char tstr_str[] = "(time interval optional) and string"; +char num_str[] = "number"; + +/* report grammatical error */ +static void +gramerr(char *phrase, char *pos, char *op) +{ + fprintf(stderr, "%s expected to %s %s\n", phrase, pos, op); + lexSync(); +} + + +%} + +/*********************************************************************** + * yacc token and operator declarations + ***********************************************************************/ + +%expect 184 +%start stmnt + +%token ARROW +%token SHELL +%token ALARM +%token SYSLOG +%token PRINT +%token STOMP +%token SOME_QUANT +%token ALL_QUANT +%token PCNT_QUANT +%token LEQ_REL +%token GEQ_REL +%token NEQ_REL +%token EQ_REL +%token AND +%token SEQ +%token OR +%token ALT +%token NOT +%token RISE +%token FALL +%token MATCH +%token NOMATCH +%token RULESET +%token ELSE +%token UNKNOWN +%token OTHERWISE +%token MIN_AGGR +%token MAX_AGGR +%token AVG_AGGR +%token SUM_AGGR +%token COUNT_AGGR +%token TIME_DOM +%token INST_DOM +%token HOST_DOM +%token UNIT_SLASH +%token INTERVAL +%token <u> EVENT_UNIT +%token <u> TIME_UNIT +%token <u> SPACE_UNIT +%token <d> NUMBER +%token <s> IDENT +%token <s> STRING +%token TRU +%token FALS +%token <x> VAR + +%type <x> exp +%type <x> rule +%type <x> ruleset +%type <x> rulelist +%type <x> rulesetopt +%type <x> act +%type <x> bexp +%type <x> rexp +%type <x> actarg +%type <x> arglist +%type <x> aexp +%type <x> quant +%type <x> aggr +%type <x> num +%type <x> str +%type <i> dom +%type <x> fetch +%type <s> metric +%type <sa> hosts +%type <sa> insts +%type <t> times +%type <u> units +%type <u> unit + +%left NAME_DELIM +%left ARROW +%left AND OR SEQ ALT +%left NOT RISE FALL +%left ALL_QUANT SOME_QUANT PCNT_QUANT +%left MATCH NOMATCH +%left '>' '<' EQ_REL NEQ_REL GEQ_REL LEQ_REL +%left '+' '-' +%left '*' '/' +%left UMINUS RATE +%left SUM_AGGR AVG_AGGR MAX_AGGR MIN_AGGR COUNT_AGGR +%left SHELL ALARM SYSLOG PRINT STOMP +%left ':' '#' '@' +%left UNIT_SLASH INTERVAL + +%% + +/*********************************************************************** + * yacc productions + ***********************************************************************/ + +stmnt : /* empty */ + { parse = NULL; } + | IDENT '=' exp + { parse = statement($1, $3); + if ((agent || applet) && $3 != NULL && + ($3->op == RULE || $3->op == ACT_SEQ || + $3->op == ACT_ALT || $3->op == ACT_SHELL || + $3->op == ACT_ALARM || $3->op == ACT_SYSLOG || + $3->op == ACT_PRINT || $3->op == ACT_STOMP)) { + synerr(); + fprintf(stderr, "operator %s not allowed in agent " + "mode\n", opStrings($3->op)); + parse = NULL; + } + } + | exp + { parse = statement(NULL, $1); + if (agent) { + synerr(); + fprintf(stderr, "expressions must be named in agent " + "mode\n"); + parse = NULL; + } + } + ; + +exp : rule + { $$ = $1; } + | ruleset + { $$ = $1; } + | bexp + { $$ = $1; } + | aexp + { $$ = $1; } + | act + { $$ = $1; } + | str + { $$ = $1; } + ; + +rule : '(' rule ')' + { $$ = $2; } + | bexp ARROW act + { $$ = ruleExpr($1, $3); } + + /* error reporting */ + | error ARROW + { gramerr(bexp_str, precede, opStrings(RULE)); + $$ = NULL; } + | bexp ARROW error + { gramerr(act_str, follow, opStrings(RULE)); + $$ = NULL; } + ; + +ruleset : RULESET rulelist rulesetopt + { $$ = binaryExpr(CND_RULESET, $2, $3); } + ; + +rulelist : rule + { $$ = $1; } + | rule ELSE rulelist + /* + * use right recursion here so rules appear l-to-r in + * the expression tree to match the required order of + * evaluation + */ + { $$ = binaryExpr(CND_OR, $1, $3); } + ; + +rulesetopt : UNKNOWN ARROW act + { $$ = binaryExpr(CND_OTHER, ruleExpr(boolConst(B_TRUE), $3), boolConst(B_FALSE)); } + | OTHERWISE ARROW act + { $$ = binaryExpr(CND_OTHER, boolConst(B_FALSE), ruleExpr(boolConst(B_TRUE), $3)); } + | UNKNOWN ARROW act OTHERWISE ARROW act + { $$ = binaryExpr(CND_OTHER, ruleExpr(boolConst(B_TRUE), $3), ruleExpr(boolConst(B_TRUE), $6)); } + | /* empty */ + { $$ = NULL; } + ; + +act : '(' act ')' + { $$ = $2; } + | act SEQ act + { $$ = actExpr(ACT_SEQ, $1, $3); } + | act ALT act + { $$ = actExpr(ACT_ALT, $1, $3); } + | SHELL actarg + { $$ = actExpr(ACT_SHELL, $2, NULL); } + | SHELL num actarg /* holdoff format */ + { $$ = actExpr(ACT_SHELL, $3, $2); } + | ALARM actarg + { $$ = actExpr(ACT_ALARM, $2, NULL); } + | ALARM num actarg /* holdoff format */ + { $$ = actExpr(ACT_ALARM, $3, $2); } + | SYSLOG actarg + { do_syslog_args($2); + $$ = actExpr(ACT_SYSLOG, $2, NULL); + } + | SYSLOG num actarg /* holdoff format */ + { + do_syslog_args($3); + $$ = actExpr(ACT_SYSLOG, $3, $2); + } + | PRINT actarg + { $$ = actExpr(ACT_PRINT, $2, NULL); } + | PRINT num actarg /* holdoff format */ + { $$ = actExpr(ACT_PRINT, $3, $2); } + | STOMP actarg + { + stomping = 1; + $$ = actExpr(ACT_STOMP, $2, NULL); + } + | STOMP num actarg /* holdoff format */ + { + stomping = 1; + $$ = actExpr(ACT_STOMP, $3, $2); + } + + /* error reporting */ + | error SEQ + { gramerr(act_str, precede, opStrings(ACT_SEQ)); + $$ = NULL; } +/*** following cause harmless shift/reduce conflicts ***/ + | act SEQ error + { gramerr(act_str, follow, opStrings(ACT_SEQ)); + $$ = NULL; } + | error ALT + { gramerr(act_str, precede, opStrings(ACT_ALT)); + $$ = NULL; } + | act ALT error + { gramerr(act_str, follow, opStrings(ACT_ALT)); + $$ = NULL; } +/*** preceding cause harmless shift/reduce conflicts ***/ + | SHELL error + { gramerr(tstr_str, follow, opStrings(ACT_SHELL)); + $$ = NULL; } + | ALARM error + { gramerr(tstr_str, follow, opStrings(ACT_ALARM)); + $$ = NULL; } + | SYSLOG error + { gramerr(tstr_str, follow, opStrings(ACT_SYSLOG)); + $$ = NULL; } + | PRINT error + { gramerr(tstr_str, follow, opStrings(ACT_PRINT)); + $$ = NULL; } + | STOMP error + { gramerr(tstr_str, follow, opStrings(ACT_STOMP)); + $$ = NULL; } + ; + +actarg : arglist + { $$ = actArgExpr($1, NULL); } + ; + +arglist : STRING + { $$ = actArgList(NULL, $1); } + | STRING arglist + { $$ = actArgList($2, $1); } + ; + +bexp : '(' bexp ')' + { $$ = $2; } + | rexp + { $$ = $1; } + | quant + { $$ = $1; } + | TRU + { $$ = boolConst(B_TRUE); } + | FALS + { $$ = boolConst(B_FALSE); } + | NOT bexp + { $$ = unaryExpr(CND_NOT, $2); } + | RISE bexp + { $$ = boolMergeExpr(CND_RISE, $2); } + | FALL bexp + { $$ = boolMergeExpr(CND_FALL, $2); } + | bexp AND bexp + { $$ = binaryExpr(CND_AND, $1, $3); } + | bexp OR bexp + { $$ = binaryExpr(CND_OR, $1, $3); } + | MATCH str bexp + { /* + * note args are reversed so bexp is to the "left" + * of the operand node in the expr tree + */ + $$ = binaryExpr(CND_MATCH, $3, $2); } + | NOMATCH str bexp + { $$ = binaryExpr(CND_NOMATCH, $3, $2); } + + /* error reporting */ + | NOT error + { gramerr(bexp_str, follow, opStrings(CND_NOT)); + $$ = NULL; } + | RISE error + { gramerr(bexp_str, follow, opStrings(CND_RISE)); + $$ = NULL; } + | FALL error + { gramerr(bexp_str, follow, opStrings(CND_FALL)); + $$ = NULL; } + | MATCH error + { gramerr("regular expression", follow, opStrings(CND_MATCH)); + $$ = NULL; } + | MATCH str error + { gramerr(bexp_str, follow, "regular expression"); + $$ = NULL; } + | NOMATCH error + { gramerr("regular expression", follow, opStrings(CND_NOMATCH)); + $$ = NULL; } + | NOMATCH str error + { gramerr(bexp_str, follow, "regular expression"); + $$ = NULL; } +/*** following cause harmless shift/reduce conflicts ***/ + | error AND + { gramerr(bexp_str, precede, opStrings(CND_AND)); + $$ = NULL; } + | bexp AND error + { gramerr(bexp_str, follow, opStrings(CND_AND)); + $$ = NULL; } + | error OR + { gramerr(bexp_str, precede, opStrings(CND_OR)); + $$ = NULL; } + | bexp OR error + { gramerr(bexp_str, follow, opStrings(CND_OR)); + $$ = NULL; } +/*** preceding cause harmless shift/reduce conflicts ***/ + ; + +quant : ALL_QUANT dom bexp + { $$ = domainExpr(CND_ALL_HOST, $2, $3); } + | SOME_QUANT dom bexp + { $$ = domainExpr(CND_SOME_HOST, $2, $3); } + | NUMBER PCNT_QUANT dom bexp + { $$ = percentExpr($1, $3, $4); } + + /* error reporting */ + | ALL_QUANT dom error + { gramerr(bexp_str, follow, quant_str); + $$ = NULL; } + | SOME_QUANT dom error + { gramerr(bexp_str, follow, quant_str); + $$ = NULL; } + | NUMBER PCNT_QUANT dom error + { gramerr(bexp_str, follow, quant_str); + $$ = NULL; } + | error PCNT_QUANT + { gramerr(num_str, precede, pcnt_str); + $$ = NULL; } + ; + +rexp : aexp EQ_REL aexp + { $$ = relExpr(CND_EQ, $1, $3); } + | aexp NEQ_REL aexp + { $$ = relExpr(CND_NEQ, $1, $3); } + | aexp '<' aexp + { $$ = relExpr(CND_LT, $1, $3); } + | aexp '>' aexp + { $$ = relExpr(CND_GT, $1, $3); } + | aexp LEQ_REL aexp + { $$ = relExpr(CND_LTE, $1, $3); } + | aexp GEQ_REL aexp + { $$ = relExpr(CND_GTE, $1, $3); } + + /* error reporting */ + | error EQ_REL + { gramerr(aexp_str, precede, opStrings(CND_EQ)); + $$ = NULL; } + | aexp EQ_REL error + { gramerr(aexp_str, follow, opStrings(CND_EQ)); + $$ = NULL; } + | error NEQ_REL + { gramerr(aexp_str, precede, opStrings(CND_NEQ)); + $$ = NULL; } + | aexp NEQ_REL error + { gramerr(aexp_str, follow, opStrings(CND_NEQ)); + $$ = NULL; } + | error '<' + { gramerr(aexp_str, precede, opStrings(CND_LT)); + $$ = NULL; } + | aexp '<' error + { gramerr(aexp_str, follow, opStrings(CND_LT)); + $$ = NULL; } + | error '>' + { gramerr(aexp_str, precede, opStrings(CND_GT)); + $$ = NULL; } + | aexp '>' error + { gramerr(aexp_str, follow, opStrings(CND_GT)); + $$ = NULL; } + | error LEQ_REL + { gramerr(aexp_str, precede, opStrings(CND_LTE)); + $$ = NULL; } + | aexp LEQ_REL error + { gramerr(aexp_str, follow, opStrings(CND_LTE)); + $$ = NULL; } + | error GEQ_REL + { gramerr(aexp_str, precede, opStrings(CND_GTE)); + $$ = NULL; } + | aexp GEQ_REL error + { gramerr(aexp_str, follow, opStrings(CND_GTE)); + $$ = NULL; } + ; + +aexp : '(' aexp ')' + { $$ = $2; } + | fetch + { $$ = $1; } + | num + { $$ = $1; } + | VAR + { $$ = $1; } + | aggr + { $$ = $1; } + | RATE aexp + { $$ = numMergeExpr(CND_RATE, $2); } + | '-' aexp %prec UMINUS + { $$ = unaryExpr(CND_NEG, $2); } + | aexp '+' aexp + { $$ = binaryExpr(CND_ADD, $1, $3); } + | aexp '-' aexp + { $$ = binaryExpr(CND_SUB, $1, $3); } + | aexp '*' aexp + { $$ = binaryExpr(CND_MUL, $1, $3); } + | aexp '/' aexp + { $$ = binaryExpr(CND_DIV, $1, $3); } + + /* error reporting */ + | RATE error + { gramerr(aexp_str, follow, opStrings(CND_RATE)); + $$ = NULL; } + | '-' error %prec UMINUS + { gramerr(aexp_str, follow, opStrings(CND_NEG)); + $$ = NULL; } +/*** following cause harmless shift/reduce conflicts ***/ + | error '+' + { gramerr(aexp_str, precede, opStrings(CND_ADD)); + $$ = NULL; } + | aexp '+' error + { gramerr(aexp_str, follow, opStrings(CND_ADD)); + $$ = NULL; } + | error '-' + { gramerr(aexp_str, precede, opStrings(CND_SUB)); + $$ = NULL; } + | aexp '-' error + { gramerr(aexp_str, follow, opStrings(CND_SUB)); + $$ = NULL; } + | error '*' + { gramerr(aexp_str, precede, opStrings(CND_MUL)); + $$ = NULL; } + | aexp '*' error + { gramerr(aexp_str, follow, opStrings(CND_MUL)); + $$ = NULL; } + | error '/' + { gramerr(aexp_str, precede, opStrings(CND_DIV)); + $$ = NULL; } + | aexp '/' error + { gramerr(aexp_str, follow, opStrings(CND_DIV)); + $$ = NULL; } +/*** preceding cause harmless shift/reduce conflicts ***/ + ; + +aggr : SUM_AGGR dom aexp + { $$ = domainExpr(CND_SUM_HOST, $2, $3); } + | AVG_AGGR dom aexp + { $$ = domainExpr(CND_AVG_HOST, $2, $3); } + | MAX_AGGR dom aexp + { $$ = domainExpr(CND_MAX_HOST, $2, $3); } + | MIN_AGGR dom aexp + { $$ = domainExpr(CND_MIN_HOST, $2, $3); } + | COUNT_AGGR dom bexp + { $$ = domainExpr(CND_COUNT_HOST, $2, $3); } + + /* error reporting */ + | SUM_AGGR dom error + { gramerr(aexp_str, follow, aggr_str); + $$ = NULL; } + | AVG_AGGR dom error + { gramerr(aexp_str, follow, aggr_str); + $$ = NULL; } + | MAX_AGGR dom error + { gramerr(aexp_str, follow, aggr_str); + $$ = NULL; } + | MIN_AGGR dom error + { gramerr(aexp_str, follow, aggr_str); + $$ = NULL; } + ; + +dom : HOST_DOM + { $$ = HOST_DOM; } + | INST_DOM + { $$ = INST_DOM; } + | TIME_DOM + { $$ = TIME_DOM; } + ; + +fetch : metric hosts insts times + { $$ = fetchExpr($1, $2, $3, $4); } + ; + +metric : IDENT + { $$ = $1; } + ; + +hosts : /* empty */ + { $$.n = 0; + $$.ss = NULL; } + | hosts ':' IDENT + { $$.n = $1.n + 1; + $$.ss = (char **) ralloc($1.ss, $$.n * sizeof(char *)); + $$.ss[$1.n] = $3; } + + /* error reporting */ + | hosts ':' error + { gramerr(host_str, follow, ":"); + $$.n = 0; + $$.ss = NULL; } + ; + +insts : /* empty */ + { $$.n = 0; + $$.ss = NULL; } + | insts '#' IDENT + { $$.n = $1.n + 1; + $$.ss = (char **) ralloc($1.ss, $$.n * sizeof(char *)); + $$.ss[$1.n] = $3; } + + /* error reporting */ + | insts '#' error + { gramerr(inst_str, follow, "#"); + $$.n = 0; + $$.ss = NULL; } + ; + +times : /* empty */ + { $$.t1 = 0; + $$.t2 = 0; } + | '@' NUMBER + { $$.t1 = $2; + $$.t2 = $2; } + | '@' NUMBER INTERVAL NUMBER + { if ($2 <= $4) { + $$.t1 = $2; + $$.t2 = $4; + } + else { + $$.t1 = $4; + $$.t2 = $2; + } } + + /* error reporting */ + | '@' error + { gramerr(sample_str, follow, "@"); + $$.t1 = 0; + $$.t2 = 0; } + ; + +num : NUMBER units + { $$ = numConst($1, $2); } + ; + +units : /* empty */ + { $$ = noUnits; } + | units unit + { $$ = $1; + if ($2.dimSpace) { + $$.dimSpace = $2.dimSpace; + $$.scaleSpace = $2.scaleSpace; + } + else if ($2.dimTime) { + $$.dimTime = $2.dimTime; + $$.scaleTime = $2.scaleTime; + } + else { + $$.dimCount = $2.dimCount; + $$.scaleCount = $2.scaleCount; + } } + | units UNIT_SLASH unit + { $$ = $1; + if ($3.dimSpace) { + $$.dimSpace = -$3.dimSpace; + $$.scaleSpace = $3.scaleSpace; + } + else if ($3.dimTime) { + $$.dimTime = -$3.dimTime; + $$.scaleTime = $3.scaleTime; + } + else { + $$.dimCount = -$3.dimCount; + $$.scaleCount = $3.scaleCount; + } } + ; + +unit : SPACE_UNIT + { $$ = $1; } + | SPACE_UNIT '^' NUMBER + { $$ = $1; + $$.dimSpace = $3; } + | TIME_UNIT + { $$ = $1; } + | TIME_UNIT '^' NUMBER + { $$ = $1; + $$.dimTime = $3; } + | EVENT_UNIT + { $$ = $1; } + | EVENT_UNIT '^' NUMBER + { $$ = $1; + $$.dimCount = $3; } + ; + +str : STRING + { $$ = strConst($1); } + ; + +%% + diff --git a/src/pmie/src/hdr.sk b/src/pmie/src/hdr.sk new file mode 100644 index 0000000..98cd92b --- /dev/null +++ b/src/pmie/src/hdr.sk @@ -0,0 +1,37 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * fun.c + * + * These functions are generated from skeletons <file>.sk + * by the shell-script './meta'. + ***********************************************************************/ + +#include "pmapi.h" +#include "impl.h" +#if defined(HAVE_SYS_WAIT_H) +#include <sys/wait.h> +#endif +#include "dstruct.h" +#include "pragmatics.h" +#include "fun.h" +#include "show.h" +#include "stomp.h" + + diff --git a/src/pmie/src/lexicon.c b/src/pmie/src/lexicon.c new file mode 100644 index 0000000..898205e --- /dev/null +++ b/src/pmie/src/lexicon.c @@ -0,0 +1,925 @@ +/*********************************************************************** + * lexicon.c - lexical scanner + * + * There is enough lookahead to enable use of '/' as both an arithmetic + * and units operator. Nested macro expansion is supported using a stack + * of input contexts (see definition of LexIn in file syntax.y). + *********************************************************************** + * + * 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. + * + * 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 <ctype.h> +#include <string.h> +#include <limits.h> +#include <math.h> + +#include "dstruct.h" +#include "syntax.h" +#include "grammar.h" +#include "lexicon.h" +#include "pragmatics.h" + + +/*********************************************************************** + * type definitions + ***********************************************************************/ + +/* for table of lexical items */ +typedef struct { + char *key; + int token; +} LexEntry1; + +/* for another table of lexical items */ +typedef struct { + char *key; + int token; + int scale; +} LexEntry2; + + +/*********************************************************************** + * constants + ***********************************************************************/ + +static LexEntry1 domtab[] = { + {"host", HOST_DOM}, + {"inst", INST_DOM}, + {"sample", TIME_DOM}, + {NULL, 0} +}; + +static LexEntry1 quantab[] = { + {"sum", SUM_AGGR}, + {"avg", AVG_AGGR}, + {"max", MAX_AGGR}, + {"min", MIN_AGGR}, + {"count", COUNT_AGGR}, + {"all", ALL_QUANT}, + {"some", SOME_QUANT}, + {"%", PCNT_QUANT}, + {NULL, 0} +}; + +static LexEntry1 optab[] = { + {"true", TRU}, + {"false", FALS}, + {"rate", RATE}, + {"shell", SHELL}, + {"alarm", ALARM}, + {"syslog", SYSLOG}, + {"print", PRINT}, + {"stomp", STOMP}, + {"rising", RISE}, + {"falling", FALL}, + {"match_inst", MATCH}, + {"nomatch_inst",NOMATCH}, + {"ruleset", RULESET}, + {"else", ELSE}, + {"unknown", UNKNOWN}, + {"otherwise", OTHERWISE}, + {NULL, 0} +}; + +static LexEntry2 unitab[] = { + {"byte", SPACE_UNIT, PM_SPACE_BYTE}, + {"Kbyte", SPACE_UNIT, PM_SPACE_KBYTE}, + {"Mbyte", SPACE_UNIT, PM_SPACE_MBYTE}, + {"Gbyte", SPACE_UNIT, PM_SPACE_GBYTE}, + {"Tbyte", SPACE_UNIT, PM_SPACE_TBYTE}, + {"nsec", TIME_UNIT, PM_TIME_NSEC}, + {"usec", TIME_UNIT, PM_TIME_USEC}, + {"msec", TIME_UNIT, PM_TIME_MSEC}, + {"sec", TIME_UNIT, PM_TIME_SEC}, + {"min", TIME_UNIT, PM_TIME_MIN}, + {"hour", TIME_UNIT, PM_TIME_HOUR}, + {"count", EVENT_UNIT, 0}, + {"Kcount", EVENT_UNIT, 3}, + {"Mcount", EVENT_UNIT, 6}, + {"nanosec", TIME_UNIT, PM_TIME_NSEC}, + {"nanosecond", TIME_UNIT, PM_TIME_NSEC}, + {"microsec", TIME_UNIT, PM_TIME_USEC}, + {"microsecond", TIME_UNIT, PM_TIME_USEC}, + {"millisec", TIME_UNIT, PM_TIME_MSEC}, + {"millisecond", TIME_UNIT, PM_TIME_MSEC}, + {"second", TIME_UNIT, PM_TIME_SEC}, + {"minute", TIME_UNIT, PM_TIME_MIN}, + {NULL, 0, 0} +}; + + + +/*********************************************************************** + * variables + ***********************************************************************/ + + LexIn *lin; /* current input context */ +static char *token; /* current token buffer */ + + + +/*********************************************************************** + * local functions + ***********************************************************************/ + +/* unwind input context */ +static void +unwind(void) +{ + LexIn *tmp; + + if (lin->name) { + free(lin->name); + if (! lin->macro) + fclose(lin->stream); + } + tmp = lin; + lin = lin->prev; + free(tmp); +} + + +/* next input character */ +static int +nextc(void) +{ + int c = '\0'; + + if (lin) { + if (lin->lookin != lin->lookout) { + c = lin->look[lin->lookout++]; + if (lin->lookout >= LEX_MAX + 2) + lin->lookout = 0; + } + else { + if (lin->macro) + c = *lin->macro++; + else { + c = getc(lin->stream); + if (c == EOF) + c = '\0'; + } + if (c == '\0') { + unwind(); + return nextc(); + } + lin->cno++; + if (c == '\n') { + lin->lno++; + lin->cno = 0; + } + } +#if PCP_DEBUG && PCP_DESPERATE + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "nextc() -> \'%c\'\n", c); +#endif + return c; + } + return EOF; +} + + +/* new file input context */ +static int +inFile(char *name) +{ + FILE *f; + LexIn *t; + + t = (LexIn *) zalloc(sizeof(LexIn)); + + if (name == NULL) + f = stdin; + else { + if ((f = fopen(name, "r")) == NULL) { + fprintf(stderr, "%s: cannot open config file %s\n", pmProgname, name); + free(t); + return 0; + } + t->name = (char *) alloc(strlen(name) + 1); + strcpy(t->name, name); + } + + t->stream = f; + t->lno = 1; + t->cno = 0; + t->prev = lin; + lin = t; + return 1; +} + +#define DEREF_ERROR 0 +#define DEREF_STRING 1 +#define DEREF_BOOL 2 +#define DEREF_NUMBER 3 +/* + * dereference macro ... return one of the DEREF_* values + * for DEREF_ERROR, error is reported here + */ +static int +varDeref(char *name) +{ + Symbol s; + Expr *x; + LexIn *t; + + /* lookup macro name */ + if ((s = symLookup(&vars, name)) == NULL) { + fprintf(stderr, "undefined macro name $%s\n", name); + return DEREF_ERROR; + } + x = symValue(s); + + /* string macro */ + if (x->sem == SEM_CHAR) { + t = (LexIn *) zalloc(sizeof(LexIn)); + t->prev = lin; + lin = t; + lin->name = (char *) alloc(strlen(name) + 1); + strcpy(lin->name, name); + lin->macro = (char *) x->ring; + lin->lno = 1; + lin->cno = 0; + return DEREF_STRING; + } + + /* boolean valued macro */ + if (x->sem == SEM_BOOLEAN) { + yylval.x = x; + return DEREF_BOOL; + } + + /* constant numeric valued macro */ + if (x->sem == SEM_NUMCONST) { + /* + * need to copy the Expr as the one returned here may be freed + * later after constant folding, and we need the real macro's + * value to be available for use in later rules + */ + yylval.x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_NUMCONST); + yylval.x->smpls[0].ptr = x->smpls[0].ptr; + yylval.x->valid = 1; + return DEREF_NUMBER; + } + + /* variable numeric valued macro */ + if (x->sem == SEM_NUMVAR) { + yylval.x = x; + return DEREF_NUMBER; + } + + fprintf(stderr, "varDeref(%s): internal botch sem=%d?\n", name, x->sem); + dumpExpr(x); + exit(1); +} + + +/* push character into lookahead queue */ +static void +prevc(int c) +{ + if (lin) { + lin->look[lin->lookin++] = c; + if (lin->lookin >= LEX_MAX + 2) + lin->lookin = 0; + } +} + + +/* push string into lookahead queue */ +static void +prevs(char *s) +{ + while (*s != '\0') { + lin->look[lin->lookin++] = *s++; + if (lin->lookin >= LEX_MAX + 2) + lin->lookin = 0; + } +} + +/* get IDENT after '$' ... match rules for IDENT in main scanner */ +static int +get_ident(char *namebuf) +{ + int c; + int d = 0; + int i; + char *namebuf_start = namebuf; + + c = nextc(); + if (c == '\'') { + d = c; + c = nextc(); + } + if (!isalpha(c)) { + fprintf(stderr, "macro name must begin with an alphabetic (not '%c')\n", c); + lexSync(); + return 0; + } + i = 0; + do { + if (c == '\\') c = nextc(); + *namebuf++ = c; + i++; + c = nextc(); + } while (i < LEX_MAX && c != EOF && + (isalpha(c) || isdigit(c) || c == '_' || (d && c != d))); + + if (i == LEX_MAX) { + namebuf_start[20] = '\0'; + fprintf(stderr, "macro name too long: $%s...\n", namebuf_start); + lexSync(); + return 0; + } + if (c == EOF) { + *namebuf = '\0'; + fprintf(stderr, "unexpected end of file in macro name: $%s\n", namebuf_start); + lexSync(); + return 0; + } + + if (!d) + prevc(c); + + *namebuf = '\0'; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "get_ident() -> macro name \"%s\"\n", namebuf_start); +#endif + + return 1; +} + + +/*********************************************************************** + * exported functions + ***********************************************************************/ + +/* initialize scan of new input stream */ +int +lexInit(char *fname) +{ + lin = NULL; + if (! inFile(fname)) + return 0; + token = (char *) alloc(LEX_MAX + 1); + return 1; +} + + +/* finalize scan of input stream */ +void +lexFinal(void) +{ + free(token); +} + + +/* not end of input stream? */ +int lexMore(void) +{ + return (lin != NULL); +} + + +/* discard input to next ';' or EOF */ +void +lexSync(void) +{ + int c; + + do + c = nextc(); + while (c != ';' && c != EOF) + ; + prevc(c); +} + + +/* scanner main function */ +int +yylex(void) +{ + int c, d; /* current character */ + int esc = 0; /* for escape processing */ + static int ahead = 0; /* lookahead token */ + int behind = 0; /* lookbehind token */ + LexEntry1 *lt1; /* scans through lexbuf1 */ + LexEntry2 *lt2; /* scans through lexbuf2 */ + char *p, *q; + int i; + char nbuf[LEX_MAX+1]; /* for getting macro name */ + + /* token from previous invocation */ + if (ahead) { + c = ahead; + ahead = 0; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> TOKEN (ahead) \'%c\'\n", c); +#endif + return c; + } + + /* scan token from input */ + c = nextc(); + while (c != EOF) { + + /* skip blanks */ + while (isspace(c)) + c = nextc(); + + /* scan C style comment */ + if (c == '/') { + if ((d = nextc()) != '*') + prevc(d); + else { + c = nextc(); + while (c != EOF) { + d = nextc(); + if ((c == '*') && (d == '/')) { + c = nextc(); + break; + } + c = d; + } + continue; + } + } + + /* scan C++ style comment */ + if (c == '/') { + if ((d = nextc()) != '/') + prevc(d); + else { + do c = nextc(); + while (( c != '\n') && (c != EOF)); + continue; + } + } + + /* scan alphanumeric */ + if (isalpha(c) || (c == '\'') || (c == '%')) { + d = c; + if (d == '\'') c = nextc(); + i = 0; + do { + if (c == '$') { + /* macro embedded in identifier */ + int sts; + if (!get_ident(nbuf)) + return EOF; + sts = varDeref(nbuf); + if (sts == DEREF_ERROR) { + /* error reporting in varDeref() */ + lexSync(); + return EOF; + } + else if (sts != DEREF_STRING) { + synerr(); + fprintf(stderr, "macro $%s not string valued as expected\n", nbuf); + lexSync(); + return EOF; + } + c = nextc(); + } + else { + if (c == '\\') c = nextc(); + token[i++] = c; + c = nextc(); + } + } while ((isalpha(c) || isdigit(c) || + c == '.' || c == '_' || c == '\\' || c == '$' || + (d == '\'' && c != d)) && + (i < LEX_MAX)); + token[i] = '\0'; + if (d == '\'') c = nextc(); + + /* + * recognize keywords associated with units of space, time + * and count, see unitab[] + */ + if (d != '\'') { + lt2 = &unitab[0]; + if (i > 0 && token[i-1] == 's') + i--; + do { + if (strlen(lt2->key) == i && + strncmp(token, lt2->key, i) == 0) { + + /* if looking ahead after '/', return UNIT_SLASH */ + if (behind == '/') { + prevs(&token[0]); + prevc(c); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"/\"\n"); +#endif + return UNIT_SLASH; + } + prevc(c); + + yylval.u = noUnits; + switch (lt2->token) { + case SPACE_UNIT: + yylval.u.dimSpace = 1; + yylval.u.scaleSpace = lt2->scale; + break; + case TIME_UNIT: + yylval.u.dimTime = 1; + yylval.u.scaleTime = lt2->scale; + break; + case EVENT_UNIT: + yylval.u.dimCount = 1; + yylval.u.scaleCount = lt2->scale; + break; + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> UNITS \"%s\"\n", pmUnitsStr(&yylval.u)); +#endif + return lt2->token; + } + lt2++; + } while (lt2->key); + } + + /* if looking ahead, return previous token */ + if (behind) { + prevs(&token[0]); + prevc(c); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> TOKEN (behind) \'%c\'\n", behind); +#endif + return behind; + } + prevc(c); + + /* recognize aggregation and quantification */ + if (d != '\'') { + if ((p = strchr(token, '_')) != NULL) { + *p = '\0'; + lt1 = &quantab[0]; + do { + if (strcmp(&token[0], lt1->key) == 0) { + c = lt1->token; + q = p + 1; + lt1 = &domtab[0]; + do { + if (strcmp(q, lt1->key) == 0) { + ahead = lt1->token; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"%s\'\n", token); +#endif + return c; + } + lt1++; + } while (lt1->key); + break; + } + lt1++; + } while (lt1->key); + *p = '_'; + } + + /* recognize other reserved word */ + lt1 = &optab[0]; + do { + if (strcmp(&token[0], lt1->key) == 0) { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> RESERVED-WORD \"%s\"\n", token); +#endif + return lt1->token; + } + lt1++; + } while (lt1->key); + } + + /* recognize identifier */ + yylval.s = (char *) alloc(strlen(&token[0]) + 1); + strcpy(yylval.s, &token[0]); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> IDENT \"%s\"\n", token); +#endif + return IDENT; + } + + /* if looking ahead, return preceding token */ + if (behind) { + prevc(c); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> TOKEN (behind) \'%c\'\n", behind); +#endif + return behind; + } + + /* special case for .[0-9]... number without leading [0-9] */ + if (c == '.') { + c = nextc(); + if (isdigit(c)) { + /* note prevc() implements a FIFO, not a stack! */ + prevc('.'); /* push back period */ + prevc(c); /* push back digit */ + c = '0'; /* start with fake leading zero */ + } + else + prevc(c); /* not a digit after period, push back */ + } + + /* scan NUMBER */ + if (isdigit(c)) { + int flote = 0; + i = 0; + do { + token[i++] = c; + c = nextc(); + if ((flote == 0) && (c == '.') && (i < LEX_MAX)) { + c = nextc(); + if (c == '.') + prevc(c); /* INTERVAL token */ + else { + flote = 1; + token[i++] = '.'; + } + } + if ((flote <= 1) && (i < LEX_MAX) && ((c == 'e') || (c == 'E'))) { + flote = 2; + token[i++] = c; + c = nextc(); + } + if ((flote <= 2) && (c == '-') && (i < LEX_MAX)) { + flote = 3; + token[i++] = c; + c = nextc(); + } + } while (isdigit(c) && (i < LEX_MAX)); + prevc(c); + token[i] = '\0'; + yylval.d = strtod(&token[0], NULL); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> NUMBER %g\n", yylval.d); +#endif + return NUMBER; + } + + /* scan string */ + if (c == '"') { + yylval.s = NULL; + i = 0; + c = nextc(); + while ((c != '"') && (c != EOF) && (i < LEX_MAX)) { + + /* escape character */ + if (c == '\\') { + esc = 1; + c = nextc(); + switch (c) { + case 'n': + c = '\n'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'b': + c = '\b'; + break; + case 'r': + c = '\r'; + break; + case 'f': + c = '\f'; + break; + case 'a': + c = '\a'; + break; + } + } + else + esc = 0; + + /* macro embedded in string */ + if (c == '$' && !esc) { + int sts; + if (!get_ident(nbuf)) + return EOF; + sts = varDeref(nbuf); + if (sts == DEREF_ERROR) { + /* error reporting in varDeref() */ + lexSync(); + return EOF; + } + else if (sts != DEREF_STRING) { + synerr(); + fprintf(stderr, "macro $%s not string valued as expected\n", nbuf); + lexSync(); + return EOF; + } + c = nextc(); + } + + /* add character to string */ + yylval.s = (char *) ralloc(yylval.s, i+2); + yylval.s[i++] = c; + c = nextc(); + } + if (i == 0) { + /* special case for null string */ + yylval.s = (char *) ralloc(yylval.s, 1); + } + yylval.s[i++] = '\0'; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> STRING \"%s\"\n", yylval.s); +#endif + return STRING; + } + + /* scan operator */ + switch (c) { + case ';': + case '}': + do + d = nextc(); + while (isspace(d)); + if (d == '.') { + while (lin) + unwind(); + } + else + prevc(d); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> END-OF-RULE\n"); +#endif + return EOF; + + case '$': + if (!get_ident(nbuf)) + return EOF; + switch (varDeref(nbuf)) { + case DEREF_ERROR: + lexSync(); + return EOF; + case DEREF_STRING: + c = nextc(); + continue; + case DEREF_BOOL: +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> (boolean) macro $%s\n", nbuf); +#endif + return VAR; + case DEREF_NUMBER: +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> (numeric) macro $%s\n", nbuf); +#endif + return VAR; + } + /*NOTREACHED*/ + break; + + case '/': + behind = c; + c = nextc(); + continue; + case '-': + if ((d = nextc()) == '>') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"->\"\n"); +#endif + return ARROW; + } + prevc(d); + break; + case '=': + if ((d = nextc()) == '=') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"==\"\n"); +#endif + return EQ_REL; + } + prevc(d); + break; + case '!': + if ((d = nextc()) == '=') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"!=\"\n"); +#endif + return NEQ_REL; + } + prevc(d); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"!\"\n"); +#endif + return NOT; + case '<': + if ((d = nextc()) == '=') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"<=\"\n"); +#endif + return LEQ_REL; + } + prevc(d); + break; + case '>': + if ((d = nextc()) == '=') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \">=\"\n"); +#endif + return GEQ_REL; + } + prevc(d); + break; + case '&': + if ((d = nextc()) == '&') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"&&\"\n"); +#endif + return AND; + } + prevc(d); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"&\"\n"); +#endif + return SEQ; + case '|': + if ((d = nextc()) == '|') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"||\"\n"); +#endif + return OR; + } + prevc(d); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"|\"\n"); +#endif + return ALT; + case '.': + if ((d = nextc()) == '.') { +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> OPERATOR \"..\"\n"); +#endif + return INTERVAL; + } + prevc(d); + break; + + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + if (c == EOF) + fprintf(stderr, "yylex() -> EOF\n"); + else + fprintf(stderr, "yylex() -> TOKEN \'%c\' (0x%x)\n", c, c & 0xff); + } +#endif + return c; + } + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "yylex() -> EOF\n"); +#endif + return EOF; +} + + + diff --git a/src/pmie/src/lexicon.h b/src/pmie/src/lexicon.h new file mode 100644 index 0000000..7c2c16a --- /dev/null +++ b/src/pmie/src/lexicon.h @@ -0,0 +1,71 @@ +/* + * 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 + */ + +/*********************************************************************** + * lexicon.h - lexical scanner + ***********************************************************************/ + +#ifndef LEXICON_H +#define LEXICON_H + +/*********************************************************************** + * ONLY FOR USE BY: lexicon.c and syntax.c + ***********************************************************************/ + +#define LEX_MAX 254 /* max length of token */ + +/* scanner input context stack entry */ +typedef struct lexin { + struct lexin *prev; /* calling context on stack */ + FILE *stream; /* rule input stream */ + char *macro; /* input from macro definition */ + char *name; /* file/macro name */ + int lno; /* current line number */ + int cno; /* current column number */ + int lookin; /* lookahead buffer input index */ + int lookout; /* lookahead buffer output index */ + signed char look[LEX_MAX + 2]; /* lookahead ring buffer */ +} LexIn; + +extern LexIn *lin; /* current input context */ + + +/*********************************************************************** + * public + ***********************************************************************/ + +/* initialize scan of new input file */ +int lexInit(char *); + +/* finalize scan of input stream */ +void lexFinal(void); + +/* not end of input stream? */ +int lexMore(void); + +/* discard input until ';' or EOF */ +void lexSync(void); + +/* scanner main function */ +int yylex(void); + +/* yacc parser */ +int yyparse(void); + +#endif /* LEXICON_H */ + diff --git a/src/pmie/src/logger.h b/src/pmie/src/logger.h new file mode 100644 index 0000000..534f4dc --- /dev/null +++ b/src/pmie/src/logger.h @@ -0,0 +1,83 @@ +/* + * Provided by Alan Hoyt <ahoyt@moser-inc.com> as part of the Solaris port, + * this code came from + * http://www.mit.edu/afs/sipb/service/logging/arch/sun4x_55/build/zephyr/clients/syslogd/syslogd.c-test1 + * + * Copyright (c) 1983, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define NOPRI 0x10 /* the "no priority" priority */ +#define LOG_MAKEPRI(f,p) (((f) << 3) + (p)) +#define LOG_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0) /* mark "facility" */ + +#ifndef LOG_PRIMASK +#define LOG_PRIMASK 0x07 +#endif +#ifndef LOG_FACMASK +#define LOG_FACMASK 0x03f8 +#endif + +typedef struct code { + char *c_name; + int c_val; +} CODE; + +struct code prioritynames[] = { + { "panic", LOG_EMERG }, + { "alert", LOG_ALERT }, + { "crit", LOG_CRIT }, + { "error", LOG_ERR }, + { "warning", LOG_WARNING }, + { "notice", LOG_NOTICE }, + { "info", LOG_INFO }, + { "debug", LOG_DEBUG }, + { "none", NOPRI }, + { "emerg", LOG_EMERG }, + { "err", LOG_ERR }, + { "warn", LOG_WARNING }, + { NULL, -1 } +}; + +struct code facilitynames[] = { + { "daemon", LOG_DAEMON }, +#ifndef IS_MINGW + { "kern", LOG_KERN }, + { "user", LOG_USER }, + { "mail", LOG_MAIL }, + { "auth", LOG_AUTH }, + { "syslog", LOG_SYSLOG }, + { "lpr", LOG_LPR }, + { "news", LOG_NEWS }, + { "uucp", LOG_UUCP }, + { "cron", LOG_CRON }, + { "reserved", -1 }, + { "reserved", -1 }, + { "reserved", -1 }, + { "cron", LOG_CRON }, + { "local0", LOG_LOCAL0 }, + { "local1", LOG_LOCAL1 }, + { "local2", LOG_LOCAL2 }, + { "local3", LOG_LOCAL3 }, + { "local4", LOG_LOCAL4 }, + { "local5", LOG_LOCAL5 }, + { "local6", LOG_LOCAL6 }, + { "local7", LOG_LOCAL7 }, + { "security", LOG_AUTH }, + { "mark", LOG_MARK }, +#endif + { NULL, -1 } +}; + diff --git a/src/pmie/src/match_inst.c b/src/pmie/src/match_inst.c new file mode 100644 index 0000000..26a1d1c --- /dev/null +++ b/src/pmie/src/match_inst.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 1999 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> +#include <regex.h> +#include "pmapi.h" /* _pmCtime only */ +#include "impl.h" /* _pmCtime only */ +#include "dstruct.h" +#include "fun.h" +#include "show.h" + +/* + * x-arg1 is the bexp, x->arg2 is the regex + */ +void +cndMatch_inst(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + Boolean *ip1; + Boolean *op; + int n; + int i; + int sts; + int mi; + Metric *m; + + EVALARG(arg1) + ROTATE(x) + + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "match_inst(" PRINTF_P_PFX "%p): regex handle=" PRINTF_P_PFX "%p desire %s\n", + x, arg2->ring, x->op == CND_MATCH ? "match" : "nomatch"); + dumpExpr(x); + } +#endif + + if (arg2->sem != SEM_REGEX) { + fprintf(stderr, "cndMatch_inst: internal botch arg2 not SEM_REGEX?\n"); + dumpExpr(arg2); + exit(1); + } + + if (arg1->tspan > 0) { + + mi = 0; + m = &arg1->metrics[mi++]; + i = 0; + ip1 = (Boolean *)(&arg1->smpls[0])->ptr; + op = (Boolean *)(&x->smpls[0])->ptr; + + for (n = 0; n < arg1->tspan; n++) { + + if (!arg2->valid || !arg1->valid) { + *op++ = B_UNKNOWN; + } + else if (x->e_idom <= 0) { + *op++ = B_FALSE; + } + else { + while (i >= m->m_idom) { + /* + * no more values, next metric + */ + m = &arg1->metrics[mi++]; + i = 0; + } + + if (m->inames == NULL) { + *op++ = B_FALSE; + } + else { + sts = regexec((regex_t *)arg2->ring, m->inames[i], 0, NULL, 0); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + if (x->op == CND_MATCH && sts != REG_NOMATCH) { + fprintf(stderr, "match_inst: inst=\"%s\" match && %s\n", + m->inames[i], + *ip1 == B_TRUE ? "true" : + (*ip1 == B_FALSE ? "false" : + (*ip1 == B_UNKNOWN ? "unknown" : "bogus" ))); + + } + else if (x->op == CND_NOMATCH && sts == REG_NOMATCH) { + fprintf(stderr, "match_inst: inst=\"%s\" nomatch && %s\n", + m->inames[i], + *ip1 == B_TRUE ? "true" : + (*ip1 == B_FALSE ? "false" : + (*ip1 == B_UNKNOWN ? "unknown" : "bogus" ))); + } + } +#endif + if ((x->op == CND_MATCH && sts != REG_NOMATCH) || + (x->op == CND_NOMATCH && sts == REG_NOMATCH)) + *op++ = *ip1 && B_TRUE; + else + *op++ = *ip1 && B_FALSE; + } + i++; + } + ip1++; + } + x->valid++; + } + else + x->valid = 0; + + x->smpls[0].stamp = arg1->smpls[0].stamp; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "cndMatch_inst(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + diff --git a/src/pmie/src/merge.sk b/src/pmie/src/merge.sk new file mode 100644 index 0000000..ea3e814 --- /dev/null +++ b/src/pmie/src/merge.sk @@ -0,0 +1,103 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * skeleton: merge.sk + ***********************************************************************/ + +/* + * operator: @FUN + */ + +void +@FUN_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg1->smpls[1]; + Sample *os = &x->smpls[0]; + @ITYPE *ip1; + @ITYPE *ip2; + @OTYPE *op; + RealTime delta; + int n; + int i; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid >= 2 && x->tspan > 0) { + ip1 = (@ITYPE *)is1->ptr; + ip2 = (@ITYPE *)is2->ptr; + op = (@OTYPE *)os->ptr; + n = x->tspan; + @DELTA + for (i = 0; i < n; i++) { + *op = *ip1++ @OP *ip2++; + @SCALE + op++; + } + os->stamp = is1->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +@FUN_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Sample *is1 = &arg1->smpls[0]; + Sample *is2 = &arg1->smpls[1]; + Sample *os = &x->smpls[0]; + @ITYPE *ip1; + @ITYPE *ip2; + @OTYPE *op; + RealTime delta; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid >= 2) { + ip1 = (@ITYPE *)is1->ptr; + ip2 = (@ITYPE *)is2->ptr; + op = (@OTYPE *)os->ptr; + @DELTA + *op = *ip1 @OP *ip2; + @SCALE + os->stamp = is1->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + + diff --git a/src/pmie/src/meta b/src/pmie/src/meta new file mode 100755 index 0000000..00074ef --- /dev/null +++ b/src/pmie/src/meta @@ -0,0 +1,299 @@ +#!/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 +# +# Generate evaluator functions from skeletons. +# + +############## +# procedures # +############## + +CULLCOPYRIGHT="/^ \* Copyright.* Silicon Graphics.*$/d" + +_fetch() +{ +fin=fetch.sk +sed -e "$CULLCOPYRIGHT" $fin >> $fout +} + +_misc() +{ +fin=misc.sk +sed -e "$CULLCOPYRIGHT" \ + -e "s/@ITYPE/$itype/g" \ + -e "s/@OTYPE/$otype/g" \ + $fin >> $fout +} + +_aggr() +{ +fin=aggregate.sk +sed -e "$CULLCOPYRIGHT" \ + -e "s/@FUN/$fun/g" \ + -e "s/@ITYPE/$itype/g" \ + -e "s/@OTYPE/$otype/g" \ + -e "s/@TTYPE/$ttype/g" \ + -e "s/@TOP/$top/g" \ + -e "s/@LOOP/$loop/g" \ + -e "s/@BOT/$bot/g" \ + -e "s/@NOTVALID/$notvalid/g" \ + $fin >> $fout +} + +_unary() +{ +fin=unary.sk +sed -e "$CULLCOPYRIGHT" \ + -e "s/@FUN/$fun/g" \ + -e "s/@ITYPE/$itype/g" \ + -e "s/@OTYPE/$otype/g" \ + -e "s/@OP/$op/g" \ + $fin >> $fout +} + +_binary() +{ +fin=binary.sk +sed -e "$CULLCOPYRIGHT" \ + -e "s/@FUN/$fun/g" \ + -e "s/@ITYPE/$itype/g" \ + -e "s/@OTYPE/$otype/g" \ + -e "s/@OP/$op/g" \ + $fin >> $fout +} + +_merge() +{ +fin=merge.sk +if [ -z "$scale" ] +then + sed -e '/RealTime/d' $fin +else + cat $fin +fi \ +| sed -e "$CULLCOPYRIGHT" \ + -e "s/@FUN/$fun/g" \ + -e "s/@ITYPE/$itype/g" \ + -e "s/@OTYPE/$otype/g" \ + -e "s/@OP/$op/g" \ + -e "s/@DELTA/$delta/g" \ + -e "s/@SCALE/$scale/g" \ + >> $fout +} + +_act() +{ +fin=act.sk +sed -e "$CULLCOPYRIGHT" $fin >> $fout +} + + + +######## +# main # +######## + +fout=fun.c +rm -f $fout +cat hdr.sk > $fout + + +# +# fetch +# +_fetch + +# +# rule and delay +# +itype=double +otype=double + +_misc + +# +# aggregation operators +# +itype=double +otype=double +ttype=double +notvalid="x->valid = 0;" + +fun=cndSum +top="a = *ip;" +loop="a += *ip;" +bot="*op++ = a;" +_aggr + +fun=cndAvg +top="a = *ip;" +loop="a += *ip;" +bot="*op++ = a \/ n;" +_aggr + +fun=cndMax +top="a = *ip;" +loop="if (*ip > a) a = *ip;" +bot="*op++ = a;" +_aggr + +fun=cndMin +top="a = *ip;" +loop="if (*ip < a) a = *ip;" +bot="*op++ = a;" +_aggr + +# +# arithmetic operators +# +itype=double +otype=double +ttype=double + +fun=cndNeg +op="OP(x) -(x)" +_unary + +fun=cndAdd +op="OP(x,y) ((x) + (y))" +_binary + +fun=cndSub +op="OP(x,y) ((x) - (y))" +_binary + +fun=cndMul +op="OP(x,y) ((x) * (y))" +_binary + +fun=cndDiv +op="OP(x,y) ((x) \/ (y))" +_binary + +fun=cndRate +delta="delta = is1->stamp - is2->stamp;" +op="-" +scale="*op = *op \\/ delta;" +_merge + +# +# relational operators +# +itype=double +otype=Boolean +ttype=Boolean + +fun=cndEq +op="OP(x,y) ((x) == (y))" +_binary + +fun=cndNeq +op="OP(x,y) ((x) != (y))" +_binary + +fun=cndLt +op="OP(x,y) ((x) < (y))" +_binary + +fun=cndLte +op="OP(x,y) ((x) <= (y))" +_binary + +fun=cndGt +op="OP(x,y) ((x) > (y))" +_binary + +fun=cndGte +op="OP(x,y) ((x) >= (y))" +_binary + +# +# boolean connectives +# +itype=Boolean +otype=Boolean +ttype=Boolean + +fun=cndNot +op="OP(x) (((x) == B_TRUE || (x) == B_FALSE) ? !(x) : B_UNKNOWN)" +_unary + +fun=cndRise +delta="" +op=">" +scale="" +_merge + +fun=cndFall +delta="" +op="<" +scale="" +_merge + +# +# quantifiers +# +itype=Boolean +otype=Boolean +ttype=Boolean + +fun=cndAll +top="a = *ip;" +loop="if (*ip == B_FALSE) a = B_FALSE;\\ + else if (*ip == B_UNKNOWN \\&\\& a != B_UNKNOWN) a = B_UNKNOWN;" +bot="*op++ = a;" +notvalid="*op++ = B_UNKNOWN; os->stamp = is->stamp; x->valid++;" +_aggr + +fun=cndSome +top="a = *ip;" +loop="if (*ip == B_TRUE) a = B_TRUE;\\ + else if (*ip == B_UNKNOWN \\&\\& a != B_UNKNOWN) a = B_UNKNOWN;" +bot="*op++ = a;" +notvalid="*op++ = B_UNKNOWN; os->stamp = is->stamp; x->valid++;" +_aggr + +fun=cndPcnt +ttype='int ' +top="a = *ip;" +loop="a += *ip;" +bot="*op++ = (a >= (int)(0.5 + *(double *)x->arg2->ring * n)) ? B_TRUE : B_FALSE;" +notvalid="*op++ = B_UNKNOWN; os->stamp = is->stamp; x->valid++;" +_aggr + +# +# truth counter +# +itype=Boolean +otype=double +notvalid="x->valid = 0;" + +fun=cndCount +top="a = *ip == B_TRUE ? 1 : 0;" +loop="if (*ip == B_TRUE) a++;" +bot="*op++ = a;" +_aggr + +# +# actions +# +_act + +# discourage changes to fun.c +# +chmod 444 $fout diff --git a/src/pmie/src/misc.sk b/src/pmie/src/misc.sk new file mode 100644 index 0000000..30095f4 --- /dev/null +++ b/src/pmie/src/misc.sk @@ -0,0 +1,176 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * skeleton: misc.sk + ***********************************************************************/ + +#include <assert.h> + +/* + * operator RULE + */ + +void +rule(Expr *x) +{ + Expr *arg1 = x->arg1; + Expr *arg2 = x->arg2; + int sts; + + EVALARG(arg1) + if ((x->valid = arg1->valid) > 0) { + sts = (*(Boolean *)x->ring = *(Boolean *)arg1->ring); + if (sts == B_FALSE) + perf->eval_false++; + else if (sts == B_TRUE) { + perf->eval_true++; + EVALARG(arg2) + } + else + perf->eval_unknown++; + } + else + perf->eval_unknown++; +} + +/* + * operator CND_RULESET + */ + +void +ruleset(Expr *x) +{ + Expr *op; /* operator nodes */ + Expr *rp; /* rule or action nodes */ + Expr *save_curr; /* save-restore curr when calling rule() */ + Expr *other; /* UNKNOWN/OTHERWISE clauses */ + + x->valid = 0; + op = x->arg1; + while (op != NULL) { + if (op->op == RULE) + rp = op; + else { + assert(op->op == CND_RULESET || op->op == CND_OR); + rp = op->arg1; + } + assert(rp->op == RULE); + save_curr = curr; + curr = rp; + rule(rp); + curr = save_curr; + if (rp->arg1->valid) { + x->valid = rp->arg1->valid; + *(Boolean *)x->ring = *(Boolean *)rp->arg1->ring; + if (x->valid > 0 && *(Boolean *)x->ring == B_TRUE) { + /* predicate is true, so stop evaluation */ + return; + } + } + if (op->op == RULE) + break; + op = op->arg2; + } + + if (x->arg2 == NULL) + /* no OTHERWISE or UNKNOWN clauses */ + return; + + other = x->arg2; + assert(other->op == CND_OTHER); + + if (x->valid == 0) { + /* + * all predicates are B_UNKNOWN, so do the UNKNOWN action if any + */ + if (other->arg1->op != NOP) { + rp = other->arg1; + save_curr = curr; + curr = rp; + rule(rp); + curr = save_curr; + return; + } + } + + /* + * no predicate is B_TRUE and either some predicate is B_FALSE + * or they are all B_UNKNOWN and there is no UNKNOWN action ... + * so do the OTHERWISE action, if any + */ + if (other->arg2->op != NOP) { + rp = other->arg2; + save_curr = curr; + curr = rp; + rule(rp); + save_curr = curr; + x->valid = rp->arg1->valid; + *(Boolean *)x->ring = *(Boolean *)rp->arg1->ring; + } +} + +/* + * operator: cndDelay + */ + +void +cndDelay_n(Expr *x) +{ + Expr *arg1 = x->arg1; + int n = arg1->tdom; + Sample *is = &arg1->smpls[n - 1]; + Sample *os = &x->smpls[0]; + @ITYPE *ip; + @OTYPE *op; + int i; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid >= n && x->tspan > 0) { + ip = (@ITYPE *)is->ptr; + op = (@OTYPE *)os->ptr; + for (i = 0; i < x->tspan; i++) + *op++ = *ip++; + os->stamp = is->stamp; + x->valid++; + } + else x->valid = 0; +} + +void +cndDelay_1(Expr *x) +{ + Expr *arg1 = x->arg1; + int n = arg1->tdom; + Sample *is = &arg1->smpls[n - 1]; + Sample *os = &x->smpls[0]; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid >= n) { + *(@OTYPE *)os->ptr = *(@ITYPE *)is->ptr; + os->stamp = is->stamp; + x->valid++; + } + else x->valid = 0; +} + + diff --git a/src/pmie/src/pmie.c b/src/pmie/src/pmie.c new file mode 100644 index 0000000..b5150fa --- /dev/null +++ b/src/pmie/src/pmie.c @@ -0,0 +1,942 @@ +/*********************************************************************** + * pmie.c - performance inference engine + *********************************************************************** + * + * Copyright (c) 2013-2014 Red Hat, Inc. + * 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. + */ + +/* + * pmie debug flags: + * APPL0 - lexical scanning + * APPL1 - parse/expression tree construction + * APPL2 - expression execution + */ + +#include <ctype.h> +#include <limits.h> +#include <sys/stat.h> +#include "pmapi.h" +#include "impl.h" + +#include "dstruct.h" +#include "stomp.h" +#include "syntax.h" +#include "pragmatics.h" +#include "eval.h" +#include "show.h" + + +/*********************************************************************** + * constants + ***********************************************************************/ + +#define LINE_LENGTH 255 /* max length of command token */ +#define PROC_FNAMESIZE 20 /* from proc pmda - proc.h */ + +static char *prompt = "pmie> "; +static char *intro = "Performance Co-Pilot Inference Engine (pmie), " + "Version %s\n\n%s%s"; +char *clientid; + +static FILE *logfp; +static char logfile[MAXPATHLEN+1]; +static char perffile[MAXPATHLEN+1]; /* /var/tmp/<pid> file name */ +static char *username; + +static char menu[] = +"pmie debugger commands\n\n" +" f [file-name] - load expressions from given file or stdin\n" +" l [expr-name] - list named expression or all expressions\n" +" r [interval] - run for given or default interval\n" +" S time-spec - set start time for run\n" +" T time-spec - set default interval for run command\n" +" v [expr-name] - print subexpression used for %h, %i and\n" +" %v bindings\n" +" h or ? - print this menu of commands\n" +" q - quit\n\n"; + +/*********************************************************************** + * command line usage + ***********************************************************************/ + +static int +override(int opt, pmOptions *opts) +{ + if (opt == 'a' || opt == 'h' || opt == 'H' || opt == 'V') + return 1; + return 0; +} + +static pmLongOptions longopts[] = { + PMAPI_OPTIONS_HEADER("General options"), + PMOPT_ALIGN, + PMOPT_ARCHIVE, + PMOPT_DEBUG, + PMOPT_HOST, + PMOPT_NAMESPACE, + PMOPT_ORIGIN, + PMOPT_START, + PMOPT_FINISH, + PMOPT_INTERVAL, + PMOPT_TIMEZONE, + PMOPT_HOSTZONE, + PMOPT_HELP, + PMAPI_OPTIONS_HEADER("Runtime options"), + { "check", 0, 'C', 0, "parse configuration and exit" }, + { "config", 1, 'c', "FILE", "configuration file" }, + { "interact", 0, 'd', 0, "interactive debugging mode" }, + { "foreground", 0, 'f', 0, "run in the foreground, not as a daemon" }, + { "", 0, 'H', NULL }, /* was: no DNS lookup on the default hostname */ + { "", 1, 'j', "FILE", "stomp protocol (JMS) file" }, + { "logfile", 1, 'l', "FILE", "send status and error messages to FILE" }, + { "username", 1, 'U', "USER", "run as named USER in daemon mode [default pcp]" }, + PMAPI_OPTIONS_HEADER("Reporting options"), + { "buffer", 0, 'b', 0, "one line buffered output stream, stdout on stderr" }, + { "timestamp", 0, 'e', 0, "force timestamps to be reported with -V, -v or -W" }, + { "", 0, 'v', 0, "verbose mode, expression values printed" }, + { "verbose", 0, 'V', 0, "verbose mode, annotated expression values printed" }, + { "", 0, 'W', 0, "verbose mode, satisfying expression values printed" }, + { "secret-applet", 0, 'X', 0, "run in secret applet mode (thin client)" }, + { "secret-agent", 0, 'x', 0, "run in secret agent mode (summary PMDA)" }, + PMAPI_OPTIONS_END +}; + +static pmOptions opts = { + .flags = PM_OPTFLAG_STDOUT_TZ, + .short_options = "a:A:bc:CdD:efHh:j:l:n:O:S:t:T:U:vVWXxzZ:?", + .long_options = longopts, + .short_usage = "[options] [filename ...]", + .override = override, +}; + + +/*********************************************************************** + * interactive commands + ***********************************************************************/ + +/* read command input line */ +static int +readLine(char *bfr, int max) +{ + int c, i; + + /* skip blanks */ + do + c = getchar(); + while (isspace(c)); + + /* scan till end of line */ + i = 0; + while ((c != '\n') && (c != EOF) && (i < max)) { + bfr[i++] = c; + c = getchar(); + } + bfr[i] = '\0'; + return (c != EOF); +} + + +/* scan interactive command token */ +static char * +scanCmd(char **pp) +{ + char *p = *pp; + + /* skip blanks */ + while (isspace((int)*p)) + p++; + + /* single char token */ + if (isgraph((int)*p)) { + *pp = p + 1; + return p; + } + + return NULL; +} + + +/* scan interactive command argument */ +static char * +scanArg(char *p) +{ + char *q; + + /* strip leading blanks */ + while (isspace((int)*p)) + p++; + if (*p == '\0') + return NULL; + q = p; + + /* strip trailing blanks */ + while (*q != '\0') + q++; + q--; + while (isspace((int)*q)) + q--; + *(q + 1) = '\0'; + + /* return result */ + return p; +} + + +/* load rules from given file or stdin */ +static void +load(char *fname) +{ + Symbol s; + Expr *d; + int sts = 0; + int sep = __pmPathSeparator(); + char config[MAXPATHLEN+1]; + + /* search for configfile on configuration file path */ + if (fname && access(fname, F_OK) != 0) { + sts = oserror(); /* always report the first error */ + if (__pmAbsolutePath(fname)) { + fprintf(stderr, "%s: cannot access config file %s: %s\n", pmProgname, + fname, strerror(sts)); + exit(1); + } +#if PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "load: cannot access config file %s: %s\n", fname, strerror(sts)); + } +#endif + snprintf(config, sizeof(config)-1, "%s%c" "pmie" "%c%s", + pmGetConfig("PCP_SYSCONF_DIR"), sep, sep, fname); + if (access(config, F_OK) != 0) { + fprintf(stderr, "%s: cannot access config file as either %s or %s: %s\n", + pmProgname, fname, config, strerror(sts)); + exit(1); + } +#if PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "load: using standard config file %s\n", config); + } +#endif + fname = config; + } +#if PCP_DEBUG + else if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "load: using config file %s\n", + fname == NULL? "<stdin>":fname); + } +#endif + + if (perf->config[0] == '\0') { /* keep record of first config */ + if (fname == NULL) + strcpy(perf->config, "<stdin>"); + else if (realpath(fname, perf->config) == NULL) { + fprintf(stderr, "%s: failed to resolve realpath for %s: %s\n", + pmProgname, fname, osstrerror()); + exit(1); + } + } + + if (synInit(fname)) { + while ((s = syntax()) != NULL) { + d = (Expr *) symValue(symDelta); + pragmatics(s, *(RealTime *)d->smpls[0].ptr); + } + } +} + + +/* list given expression or all expressions */ +static void +list(char *name) +{ + Task *t; + Symbol *r; + Symbol s; + int i; + + if (name) { /* single named rule */ + if ( (s = symLookup(&rules, name)) ) + showSyntax(stdout, s); + else + printf("%s: error - rule \"%s\" not defined\n", pmProgname, name); + } + else { /* all rules */ + t = taskq; + while (t) { + r = t->rules; + for (i = 0; i < t->nrules; i++) { + showSyntax(stdout, *r); + r++; + } + t = t->next; + } + } +} + + +/* list binding subexpression of given expression or all expressions */ +static void +sublist(char *name) +{ + Task *t; + Symbol *r; + Symbol s; + int i; + + if (name) { /* single named rule */ + if ( (s = symLookup(&rules, name)) ) + showSubsyntax(stdout, s); + else + printf("%s: error - rule '%s' not defined\n", pmProgname, name); + } + else { /* all rules */ + t = taskq; + while (t) { + r = t->rules; + for (i = 0; i < t->nrules; i++) { + showSubsyntax(stdout, *r); + r++; + } + t = t->next; + } + } +} + + +/*********************************************************************** + * manipulate the performance instrumentation data structure + ***********************************************************************/ + +static void +stopmonitor(void) +{ + if (*perffile) + unlink(perffile); +} + +static void +startmonitor(void) +{ + void *ptr; + char *path; + int fd; + char zero = '\0'; + char pmie_dir[MAXPATHLEN]; + + /* try to create the port file directory. OK if it already exists */ + snprintf(pmie_dir, sizeof(pmie_dir), "%s%c%s", + pmGetConfig("PCP_TMP_DIR"), __pmPathSeparator(), PMIE_SUBDIR); + if (mkdir2(pmie_dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + if (oserror() != EEXIST) { + fprintf(stderr, "%s: warning cannot create stats file dir %s: %s\n", + pmProgname, pmie_dir, osstrerror()); + } + } + atexit(stopmonitor); + + /* create and initialize memory mapped performance data file */ + sprintf(perffile, "%s%c%" FMT_PID, pmie_dir, __pmPathSeparator(), getpid()); + unlink(perffile); + if ((fd = open(perffile, O_RDWR | O_CREAT | O_EXCL | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { + /* cannot create stats file; too bad, so sad, continue on without it */ + perf = &instrument; + return; + } + /* seek to struct size and write one zero */ + lseek(fd, sizeof(pmiestats_t)-1, SEEK_SET); + if (write(fd, &zero, 1) != 1) { + fprintf(stderr, "%s: Warning: write failed for stats file %s: %s\n", + pmProgname, perffile, osstrerror()); + } + + /* map perffile & associate the instrumentation struct with it */ + if ((ptr = __pmMemoryMap(fd, sizeof(pmiestats_t), 1)) == NULL) { + fprintf(stderr, "%s: memory map failed for stats file %s: %s\n", + pmProgname, perffile, osstrerror()); + perf = &instrument; + } else { + perf = (pmiestats_t *)ptr; + } + close(fd); + + path = (logfile[0] == '\0') ? "<none>" : logfile; + strncpy(perf->logfile, path, sizeof(perf->logfile)); + perf->logfile[sizeof(perf->logfile)-1] = '\0'; + strncpy(perf->defaultfqdn, dfltHostName, sizeof(perf->defaultfqdn)); + perf->defaultfqdn[sizeof(perf->defaultfqdn)-1] = '\0'; + perf->version = 1; +} + + +/*********************************************************************** + * signal handling + ***********************************************************************/ + +static void +sigintproc(int sig) +{ + __pmSetSignalHandler(SIGINT, SIG_IGN); + __pmSetSignalHandler(SIGTERM, SIG_IGN); + if (pmDebug & DBG_TRACE_DESPERATE) + __pmNotifyErr(LOG_INFO, "%s caught SIGINT or SIGTERM\n", pmProgname); + doexit = sig; +} + +static void +remap_stdout_stderr(void) +{ + int i, j; + + fflush(stderr); + fflush(stdout); + setlinebuf(stderr); + setlinebuf(stdout); + i = fileno(stdout); + close(i); + if ((j = dup(fileno(stderr))) != i) + fprintf(stderr, "%s: Warning: failed to link stdout ... " + "dup() returns %d, expected %d (stderr=%d)\n", + pmProgname, j, i, fileno(stderr)); +} + +void +logRotate(void) +{ + FILE *fp; + int sts; + + fp = __pmRotateLog(pmProgname, logfile, logfp, &sts); + if (sts != 0) { + fprintf(stderr, "pmie: PID = %" FMT_PID ", default host = %s via %s\n\n", + getpid(), dfltHostName, dfltHostConn); + remap_stdout_stderr(); + logfp = fp; + } else { + __pmNotifyErr(LOG_ERR, "pmie: log rotation failed\n"); + } +} + +static void +sighupproc(int sig) +{ + __pmSetSignalHandler(SIGHUP, sighupproc); + dorotate = 1; +} + +static void +sigbadproc(int sig) +{ + if (pmDebug & DBG_TRACE_DESPERATE) { + __pmNotifyErr(LOG_ERR, "Unexpected signal %d ...\n", sig); + fprintf(stderr, "\nProcedure call traceback ...\n"); + __pmDumpStack(stderr); + fflush(stderr); + } + stopmonitor(); + _exit(sig); +} + + +/*********************************************************************** + * command line processing - extract command line arguments & initialize + ***********************************************************************/ + +static void +getargs(int argc, char *argv[]) +{ + char *configfile = NULL; + char *commandlog = NULL; + char *subopts; + char *subopt; + char *msg; + int checkFlag = 0; + int foreground = 0; + int sts; + int c; + int bflag = 0; + int dfltConn = 0; /* default context type */ + Archive *a; + struct timeval tv, tv1, tv2; + + extern int showTimeFlag; + extern int errs; /* syntax errors from syntax.c */ + + memset(&tv, 0, sizeof(tv)); + memset(&tv1, 0, sizeof(tv1)); + memset(&tv2, 0, sizeof(tv2)); + dstructInit(); + + while ((c = pmGetOptions(argc, argv, &opts)) != EOF) { + switch (c) { + + case 'a': /* archives */ + if (dfltConn && dfltConn != PM_CONTEXT_ARCHIVE) { + /* (technically, multiple -a's are allowed.) */ + pmprintf("%s: at most one of -a or -h allowed\n", pmProgname); + opts.errors++; + break; + } + dfltConn = opts.context = PM_CONTEXT_ARCHIVE; + subopts = opts.optarg; + for ( ; ; ) { + subopt = subopts; + subopts = strchr(subopts, ','); + if (subopts != NULL) { + *subopts++ = '\0'; + } + a = (Archive *)zalloc(sizeof(Archive)); + a->fname = subopt; + if (!initArchive(a)) { + exit(1); + } + if (subopts == NULL) break; + } + foreground = 1; + break; + + case 'b': /* line buffered, stdout on stderr */ + bflag++; + break; + + case 'c': /* configuration file */ + if (interactive) { + pmprintf("%s: at most one of -c and -d allowed\n", pmProgname); + opts.errors++; + break; + } + configfile = opts.optarg; + break; + + case 'C': /* check config and exit */ + checkFlag = 1; + break; + + case 'd': /* interactive mode */ + if (configfile) { + pmprintf("%s: at most one of -c and -d allowed\n", pmProgname); + opts.errors++; + break; + } + interactive = 1; + break; + + case 'e': /* force timestamps */ + showTimeFlag = 1; + break; + + case 'f': /* in foreground, not as daemon */ + foreground = 1; + break; + + case 'H': /* deprecated: no DNS lookups */ + break; + + case 'h': /* default host name */ + if (dfltConn) { + pmprintf("%s: at most one of -a or -h allowed\n", pmProgname); + opts.errors++; + break; + } + dfltConn = opts.context = PM_CONTEXT_HOST; + dfltHostConn = opts.optarg; + dfltHostName = ""; /* unknown until newContext */ + break; + + case 'j': /* stomp protocol (JMS) config */ + stompfile = opts.optarg; + break; + + case 'l': /* alternate log file */ + if (commandlog != NULL) { + pmprintf("%s: at most one -l option is allowed\n", pmProgname); + opts.errors++; + break; + } + commandlog = opts.optarg; + isdaemon = 1; + break; + + case 'U': /* run as named user */ + username = opts.optarg; + isdaemon = 1; + break; + + case 'v': /* print values */ + verbose = 1; + break; + + case 'V': /* print annotated values */ + verbose = 2; + break; + + case 'W': /* print satisfying values */ + verbose = 3; + break; + + case 'X': /* secret applet flag */ + applet = 1; + verbose = 1; + setlinebuf(stdout); + break; + + case 'x': /* summary PMDA flag */ + agent = 1; + verbose = 1; + isdaemon = 1; + break; + } + } + + if (!opts.errors && configfile && opts.optind != argc) { + pmprintf("%s: extra filenames cannot be given after using -c\n", + pmProgname); + opts.errors++; + } + if (!opts.errors && bflag && agent) { + pmprintf("%s: the -b and -x options are incompatible\n", + pmProgname); + opts.errors++; + } + if (opts.errors) { + pmUsageMessage(&opts); + exit(1); + } + + if (foreground) + isdaemon = 0; + + hostZone = opts.tzflag; + timeZone = opts.timezone; + if (opts.interval.tv_sec || opts.interval.tv_usec) + dfltDelta = realize(opts.interval); + + if (archives || interactive) + perf = &instrument; + + if (isdaemon) { /* daemon mode */ + /* done before opening log to get permissions right */ + __pmSetProcessIdentity(username); + +#if defined(HAVE_TERMIO_SIGNALS) + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTSTP, SIG_IGN); +#endif + __pmSetSignalHandler(SIGINT, sigintproc); + __pmSetSignalHandler(SIGTERM, sigintproc); + __pmSetSignalHandler(SIGBUS, sigbadproc); + __pmSetSignalHandler(SIGSEGV, sigbadproc); + } + else { + /* need to catch these so the atexit() processing is done */ + __pmSetSignalHandler(SIGINT, sigintproc); + __pmSetSignalHandler(SIGTERM, sigintproc); + } + + if (commandlog != NULL) { + logfp = __pmOpenLog(pmProgname, commandlog, stderr, &sts); + if (realpath(commandlog, logfile) == NULL) { + fprintf(stderr, "%s: cannot find realpath for log %s: %s\n", + pmProgname, commandlog, osstrerror()); + exit(1); + } + __pmSetSignalHandler(SIGHUP, (isdaemon && !agent) ? sighupproc : SIG_IGN); + } else { + __pmSetSignalHandler(SIGHUP, SIG_IGN); + } + + /* + * -b ... force line buffering and stdout onto stderr + */ + if ((bflag || isdaemon) && !agent) + remap_stdout_stderr(); + + /* default host from leftmost archive on command line, or from + discovery after a brief connection */ + if (archives) { + a = archives; + while (a->next) + a = a->next; + dfltHostName = a->hname; /* already filled in during initArchive() */ + } else if (!dfltConn || dfltConn == PM_CONTEXT_HOST) { + if (dfltConn == 0) /* default case, no -a or -h */ + dfltHostConn = "local:"; + sts = pmNewContext(PM_CONTEXT_HOST, dfltHostConn); + /* pmcd down locally, try to extract hostname manually */ + if (sts < 0 && (!dfltConn || + !strcmp(dfltHostConn, "localhost") || + !strcmp(dfltHostConn, "local:") || + !strcmp(dfltHostConn, "unix:"))) + sts = pmNewContext(PM_CONTEXT_LOCAL, NULL); + if (sts < 0) { + fprintf(stderr, "%s: cannot find host name for %s\n" + "pmNewContext failed: %s\n", + pmProgname, dfltHostConn, pmErrStr(sts)); + exit(1); + } else { + const char *tmp = pmGetContextHostName(sts); + + if (strlen(tmp) == 0) { + fprintf(stderr, "%s: pmGetContextHostName(%d) failed\n", + pmProgname, sts); + exit(1); + } + if ((dfltHostName = strdup(tmp)) == NULL) + __pmNoMem("host name copy", strlen(tmp)+1, PM_FATAL_ERR); + pmDestroyContext(sts); + } + } + + if (!archives && !interactive) { + if (commandlog != NULL) + fprintf(stderr, "pmie: PID = %" FMT_PID ", default host = %s via %s\n\n", + getpid(), dfltHostName, dfltHostConn); + startmonitor(); + } + + /* initialize time */ + now = archives ? first : getReal() + 1.0; + zoneInit(); + reflectTime(dfltDelta); + + /* parse time window - just to check argument syntax */ + unrealize(now, &tv1); + if (archives) { + unrealize(last, &tv2); + } else { + tv2.tv_sec = INT_MAX; /* sizeof(time_t) == sizeof(int) */ + tv2.tv_usec = 0; + } + if (pmParseTimeWindow(opts.start_optarg, opts.finish_optarg, + opts.align_optarg, opts.origin_optarg, + &tv1, &tv2, + &tv, &tv2, &tv1, + &msg) < 0) { + fputs(msg, stderr); + exit(1); + } + start = realize(tv1); + stop = realize(tv2); + runTime = stop - start; + + /* when not in secret agent mode, register client id with pmcd */ + if (!agent) + clientid = __pmGetClientId(argc, argv); + + if (!interactive && opts.optind == argc) { /* stdin or config file */ + load(configfile); + } + else { /* list of 1/more filenames */ + while (opts.optind < argc) { + load(argv[opts.optind]); + opts.optind++; + } + } + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + dumpRules(); +#endif + + if (checkFlag) + exit(errs == 0 ? 0 : 1); /* exit 1 for syntax errors ... + * suggestion from + * Kevin Wang <kjw@rightsock.com> + */ + + if (isdaemon) { /* daemon mode */ + /* Note: we can no longer unilaterally close stdin here, as it + * can really confuse remap_stdout_stderr() during log rotation! + */ + if (agent) + close(fileno(stdin)); +#ifndef IS_MINGW + setsid(); /* not process group leader, lose controlling tty */ +#endif + } + + if (stomping) + stompInit(); /* connect to our message server */ + + if (agent) + agentInit(); /* initialize secret agent stuff */ + + /* really parse time window */ + if (!archives) { + now = getReal() + 1.0; + reflectTime(dfltDelta); + } + unrealize(now, &tv1); + if (archives) { + unrealize(last, &tv2); + } else { + tv2.tv_sec = INT_MAX; + tv2.tv_usec = 0; + } + if (pmParseTimeWindow(opts.start_optarg, opts.finish_optarg, + opts.align_optarg, opts.origin_optarg, + &tv1, &tv2, + &tv, &tv2, &tv1, + &msg) < 0) { + fputs(msg, stderr); + exit(1); + } + + /* set run timing window */ + start = realize(tv1); + stop = realize(tv2); + runTime = stop - start; +} + +/*********************************************************************** + * interactive (debugging) mode + ***********************************************************************/ + +static void +interact(void) +{ + int quit = 0; + char *line = (char *)zalloc(LINE_LENGTH + 2); + char *finger; + char *token; + char *msg; + RealTime rt; + struct timeval tv1, tv2; + + printf(intro, PCP_VERSION, menu, prompt); + fflush(stdout); + while (!quit && readLine(line, LINE_LENGTH)) { + finger = line; + + if ( (token = scanCmd(&finger)) ) { + switch (*token) { + + case 'f': + token = scanArg(finger); + load(token); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + dumpRules(); +#endif + break; + + case 'l': + token = scanArg(finger); + list(token); + break; + + case 'r': + token = scanArg(finger); + if (token) { + if (pmParseInterval(token, &tv1, &msg) == 0) + runTime = realize(tv1); + else { + fputs(msg, stderr); + free(msg); + break; + } + } + if (!archives) { + invalidate(); + rt = getReal(); + if (now < rt) + now = rt; + start = now; + } + stop = start + runTime; + run(); + break; + + case 'S': + token = scanArg(finger); + if (token == NULL) { + fprintf(stderr, "%s: error - argument required\n", pmProgname); + break; + } + unrealize(start, &tv1); + if (archives) { + unrealize(last, &tv2); + } else { + tv2.tv_sec = INT_MAX; + tv2.tv_usec = 0; + } + if (__pmParseTime(token, &tv1, &tv2, &tv1, &msg) < 0) { + fputs(msg, stderr); + free(msg); + break; + } + start = realize(tv1); + if (archives) + invalidate(); + break; + + case 'T': + token = scanArg(finger); + if (token == NULL) { + fprintf(stderr, "%s: error - argument required\n", pmProgname); + break; + } + if (pmParseInterval(token, &tv1, &msg) < 0) { + fputs(msg, stderr); + free(msg); + break; + } + runTime = realize(tv1); + break; + case 'q': + quit = 1; + break; + + case 'v': + token = scanArg(finger); + sublist(token); + break; + + case '?': + default: + printf("%s", menu); + } + } + if (!quit) { + printf("%s", prompt); + fflush(stdout); + } + } + free(line); +} + + +/*********************************************************************** + * main + ***********************************************************************/ + +int +main(int argc, char **argv) +{ + __pmGetUsername(&username); + setlinebuf(stdout); + + /* PCP_COUNTER_WRAP in environment enables "counter wrap" logic */ + if (getenv("PCP_COUNTER_WRAP") != NULL) + dowrap = 1; + + getargs(argc, argv); + + if (interactive) + interact(); + else + run(); + exit(0); +} diff --git a/src/pmie/src/pragmatics.c b/src/pmie/src/pragmatics.c new file mode 100644 index 0000000..23e3d1e --- /dev/null +++ b/src/pmie/src/pragmatics.c @@ -0,0 +1,1226 @@ +/* + * pragmatics.c - inference engine pragmatics analysis + * + * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2013-2014 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * 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 + * + * The analysis of how to organize the fetching of metrics (pragmatics), + * and any other parts of the inference engine sensitive to details of + * the PMAPI access are kept in this source file. + */ + +#include <math.h> +#include <ctype.h> +#include "pmapi.h" +#include "impl.h" +#include "dstruct.h" +#include "eval.h" +#include "pragmatics.h" +#if defined(HAVE_IEEEFP_H) +#include <ieeefp.h> +#endif + +extern char *clientid; + +/* for initialization of pmUnits struct */ +pmUnits noUnits; +pmUnits countUnits = { .dimCount = 1 }; + +char * +findsource(char *host) +{ + static char buf[MAXPATHLEN+MAXHOSTNAMELEN+30]; + + if (archives) { + Archive *a = archives; + while (a) { /* find archive for host */ + if (strcmp(host, a->hname) == 0) + break; + a = a->next; + } + if (a) + snprintf(buf, sizeof(buf), "archive %s (host %s)", a->fname, host); + else + snprintf(buf, sizeof(buf), "host %s in unknown archive!", host); + } + else + snprintf(buf, sizeof(buf), "host %s", host); + + return buf; +} + +/*********************************************************************** + * PMAPI context creation & destruction + ***********************************************************************/ + +int /* > 0: context handle, -1: retry later */ +newContext(char *host) +{ + Archive *a; + int sts = -1; + + if (archives) { + a = archives; + while (a) { /* find archive for host */ + if (strcmp(host, a->hname) == 0) + break; + a = a->next; + } + if (a) { /* archive found */ + if ((sts = pmNewContext(PM_CONTEXT_ARCHIVE, a->fname)) < 0) { + fprintf(stderr, "%s: cannot open archive %s\n", + pmProgname, a->fname); + fprintf(stderr, "pmNewContext: %s\n", pmErrStr(sts)); + exit(1); + } + } + else { /* no archive for host */ + fprintf(stderr, "%s: no archive for host %s\n", pmProgname, host); + exit(1); + } + } + else if ((sts = pmNewContext(PM_CONTEXT_HOST, host)) < 0) { + if (host_state_changed(host, STATE_FAILINIT) == 1) { + if (sts == -ECONNREFUSED) + fprintf(stderr, "%s: warning - pmcd " + "on host %s does not respond\n", + pmProgname, host); + else if (sts == PM_ERR_PERMISSION) + fprintf(stderr, "%s: warning - host %s does not " + "permit delivery of metrics to the local host\n", + pmProgname, host); + else if (sts == PM_ERR_CONNLIMIT) + fprintf(stderr, "%s: warning - pmcd " + "on host %s has exceeded its connection limit\n", + pmProgname, host); + else + fprintf(stderr, "%s: warning - host %s is unreachable\n", + pmProgname, host); + } + sts = -1; + } + else if (clientid != NULL) + /* register client id with pmcd */ + __pmSetClientId(clientid); + return sts; +} + + +/*********************************************************************** + * instance profile + ***********************************************************************/ + +/* equality (not symmetric) of instance names */ +static int +eqinst(char *i1, char *i2) +{ + int n1 = (int) strlen(i1); + int n2 = (int) strlen(i2); + + /* test equality of first word */ + if ((strncmp(i1, i2, n1) == 0) && ((n1 == n2) || isspace((int)i2[n1]))) + return 1; + + do { /* skip over first word */ + i2++; + n2--; + if (n2 < n1) + return 0; + } while (! isspace((int)*i2)); + + do { /* skip over spaces */ + i2++; + n2--; + if (n2 < n1) + return 0; + } while (isspace((int)*i2)); + + /* test equality of second word */ + if ((strncmp(i1, i2, n1) == 0) && ((n1 == n2) || isspace((int)i2[n1]))) + return 1; + return 0; +} + + +/*********************************************************************** + * task queue + ***********************************************************************/ + +/* find Task for new rule */ +static Task * +findTask(RealTime delta) +{ + Task *t, *u; + int n = 0; + + t = taskq; + if (t) { + while (t->next) { /* find last task in queue */ + t = t->next; + n++; + } + + /* last task in queue has same delta */ + if (t->delta == delta) + return t; + } + + u = newTask(delta, n); /* create new Task */ + if (t) { + t->next = u; + u->prev = t; + } + else + taskq = u; + return u; +} + + +/*********************************************************************** + * wait list + ***********************************************************************/ + +/* put Metric onto wait list */ +void +waitMetric(Metric *m) +{ + Host *h = m->host; + + m->next = h->waits; + m->prev = NULL; + if (h->waits) h->waits->prev = m; + h->waits = m; +} + +/* remove Metric from wait list */ +void +unwaitMetric(Metric *m) +{ + if (m->prev) m->prev->next = m->next; + else m->host->waits = m->next; + if (m->next) m->next->prev = m->prev; +} + + +/*********************************************************************** + * fetch list + ***********************************************************************/ + +/* find Host for Metric */ +static Host * +findHost(Task *t, Metric *m) +{ + Host *h; + + h = t->hosts; + while (h) { /* look for existing host */ + if (h->name == m->hname) + return h; + h = h->next; + } + + h = newHost(t, m->hname); /* add new host */ + if (t->hosts) { + h->next = t->hosts; + t->hosts->prev = h; + } + t->hosts = h; + return h; +} + +/* helper function for Extended Time Base */ +static void +getDoubleAsXTB(double *realtime, int *ival, int *mode) +{ +#define SECS_IN_24_DAYS 2073600.0 + + if (*realtime > SECS_IN_24_DAYS) { + *ival = (int)*realtime; + *mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_SEC); + } + else { + *ival = (int)(*realtime * 1000.0); + *mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_MSEC); + } +} + + +/* find Fetch bundle for Metric */ +static Fetch * +findFetch(Host *h, Metric *m) +{ + Fetch *f; + int sts; + int i; + int n; + pmID pmid = m->desc.pmid; + pmID *p; + struct timeval tv; + + /* find existing Fetch bundle */ + f = h->fetches; + + /* create new Fetch bundle */ + if (! f) { + f = newFetch(h); + if ((f->handle = newContext(symName(h->name))) < 0) { + free(f); + h->down = 1; + return NULL; + } + if (archives) { + int tmp_ival; + int tmp_mode = PM_MODE_INTERP; + getDoubleAsXTB(&h->task->delta, &tmp_ival, &tmp_mode); + + tv.tv_sec = (time_t)start; + tv.tv_usec = (int)((start - tv.tv_sec) * 1000000.0); + if ((sts = pmSetMode(tmp_mode, &tv, tmp_ival)) < 0) { + fprintf(stderr, "%s: pmSetMode failed: %s\n", pmProgname, + pmErrStr(sts)); + exit(1); + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "findFetch: fetch=0x%p host=0x%p delta=%.6f handle=%d\n", f, h, h->task->delta, f->handle); + } +#endif + } + f->next = NULL; + f->prev = NULL; + h->fetches = f; + } + + /* look for existing pmid */ + p = f->pmids; + n = f->npmids; + for (i = 0; i < n; i++) { + if (*p == pmid) break; + p++; + } + + /* add new pmid */ + if (i == n) { + p = f->pmids; + p = ralloc(p, (n+1) * sizeof(pmID)); + p[n] = pmid; + f->npmids = n + 1; + f->pmids = p; + } + + return f; +} + + +/* find Profile for Metric */ +static Profile * +findProfile(Fetch *f, Metric *m) +{ + Profile *p; + int sts; + + /* find existing Profile */ + p = f->profiles; + while (p) { + if (p->indom == m->desc.indom) { + m->next = p->metrics; + if (p->metrics) p->metrics->prev = m; + p->metrics = m; + break; + } + p = p->next; + } + + /* create new Profile */ + if (p == NULL) { + m->next = NULL; + p = newProfile(f, m->desc.indom); + p->next = f->profiles; + if (f->profiles) f->profiles->prev = p; + f->profiles = p; + p->metrics = m; + } + + /* add instances required by Metric to Profile */ + if ((sts = pmUseContext(f->handle)) < 0) { + fprintf(stderr, "%s: pmUseContext failed: %s\n", pmProgname, + pmErrStr(sts)); + exit(1); + } + + /* + * If any rule requires all instances, then ignore restricted + * instance lists from all other rules + */ + if (m->specinst == 0 && p->need_all == 0) { + sts = pmDelProfile(p->indom, 0, (int *)0); + if (sts < 0) { + fprintf(stderr, "%s: pmDelProfile failed: %s\n", pmProgname, + pmErrStr(sts)); + exit(1); + } + sts = pmAddProfile(p->indom, 0, (int *)0); + p->need_all = 1; + } + else if (m->specinst > 0 && p->need_all == 0) + sts = pmAddProfile(p->indom, m->m_idom, m->iids); + else + sts = 0; + + if (sts < 0) { + fprintf(stderr, "%s: pmAddProfile failed: %s\n", pmProgname, + pmErrStr(sts)); + exit(1); + } + + m->profile = p; + return p; +} + + +/* organize fetch bundling for given expression */ +static void +bundle(Task *t, Expr *x) +{ + Metric *m; + Host *h; + int i; + + if (x->op == CND_FETCH) { + m = x->metrics; + for (i = 0; i < x->hdom; i++) { + h = findHost(t, m); + m->host = h; + if (m->conv) /* initialized Metric */ + bundleMetric(h, m); + else /* uninitialized Metric */ + waitMetric(m); + m++; + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "bundle: task " PRINTF_P_PFX "%p nth=%d prev=" PRINTF_P_PFX "%p next=" PRINTF_P_PFX "%p delta=%.3f nrules=%d\n", + t, t->nth, t->prev, t->next, t->delta, t->nrules+1); + __dumpExpr(1, x); + m = x->metrics; + for (i = 0; i < x->hdom; i++) { + __dumpMetric(2, m); + m++; + } + } +#endif + } + else { + if (x->arg1) { + bundle(t, x->arg1); + if (x->arg2) + bundle(t, x->arg2); + } + } +} + + +/*********************************************************************** + * secret agent mode support + ***********************************************************************/ + +/* send pmDescriptor for the given Expr as a binary PDU */ +static void +sendDesc(Expr *x, pmValueSet *vset) +{ + pmDesc d; + + d.pmid = vset->pmid; + d.indom = PM_INDOM_NULL; + switch (x->sem) { + case PM_SEM_COUNTER: + case PM_SEM_INSTANT: + case PM_SEM_DISCRETE: + /* these map directly to PMAPI semantics */ + d.type = PM_TYPE_DOUBLE; + d.sem = x->sem; + d.units = x->units; + break; + + case SEM_NUMVAR: + case SEM_NUMCONST: + case SEM_BOOLEAN: + /* map to a numeric value */ + d.type = PM_TYPE_DOUBLE; + d.sem = PM_SEM_INSTANT; + d.units = x->units; + break; + + default: + fprintf(stderr, "sendDesc(%s): botch sem=%d?\n", pmIDStr(d.pmid), x->sem); + /* FALLTHROUGH */ + case SEM_UNKNOWN: + case SEM_CHAR: + case SEM_REGEX: + /* no mapping is possible */ + d.type = PM_TYPE_NOSUPPORT; + d.sem = PM_SEM_INSTANT; + d.units = noUnits; + break; + } + __pmSendDesc(STDOUT_FILENO, pmWhichContext(), &d); +} + + +/*********************************************************************** + * exported functions + ***********************************************************************/ + +/* initialize access to archive */ +int +initArchive(Archive *a) +{ + pmLogLabel label; + struct timeval tv; + int sts; + int handle; + Archive *b; + const char *tmp; + + /* setup temorary context for the archive */ + if ((sts = pmNewContext(PM_CONTEXT_ARCHIVE, a->fname)) < 0) { + fprintf(stderr, "%s: cannot open archive %s\n" + "pmNewContext failed: %s\n", + pmProgname, a->fname, pmErrStr(sts)); + return 0; + } + handle = sts; + + tmp = pmGetContextHostName(handle); + if (strlen(tmp) == 0) { + fprintf(stderr, "%s: pmGetContextHostName(%d) failed\n", + pmProgname, handle); + return 0; + } + if ((a->hname = strdup(tmp)) == NULL) + __pmNoMem("host name copy", strlen(tmp)+1, PM_FATAL_ERR); + + /* get the goodies from archive label */ + if ((sts = pmGetArchiveLabel(&label)) < 0) { + fprintf(stderr, "%s: cannot read label from archive %s\n" + "pmGetArchiveLabel failed: %s\n", + pmProgname, a->fname, pmErrStr(sts)); + pmDestroyContext(handle); + return 0; + } + a->first = realize(label.ll_start); + if ((sts = pmGetArchiveEnd(&tv)) < 0) { + fprintf(stderr, "%s: archive %s is corrupted\n" + "pmGetArchiveEnd failed: %s\n", + pmProgname, a->fname, pmErrStr(sts)); + pmDestroyContext(handle); + return 0; + } + a->last = realize(tv); + + /* check for duplicate host */ + b = archives; + while (b) { + if (strcmp(a->hname, b->hname) == 0) { + fprintf(stderr, "%s: Error: archive %s not legal - archive %s is already open " + "for host %s\n", pmProgname, a->fname, b->fname, b->hname); + pmDestroyContext(handle); + return 0; + } + b = b->next; + } + + /* put archive record on the archives list */ + a->next = archives; + archives = a; + + /* update first and last available data points */ + if (first == -1 || a->first < first) + first = a->first; + if (a->last > last) + last = a->last; + + pmDestroyContext(handle); + return 1; +} + + +/* initialize timezone */ +void +zoneInit(void) +{ + int sts; + int handle = -1; + Archive *a; + + if (timeZone) { /* TZ from timezone string */ + if ((sts = pmNewZone(timeZone)) < 0) + fprintf(stderr, "%s: cannot set timezone to %s\n" + "pmNewZone failed: %s\n", pmProgname, timeZone, + pmErrStr(sts)); + } + else if (! archives && hostZone) { /* TZ from live host */ + if ((handle = pmNewContext(PM_CONTEXT_HOST, dfltHostConn)) < 0) + fprintf(stderr, "%s: cannot set timezone from %s\n" + "pmNewContext failed: %s\n", pmProgname, + findsource(dfltHostConn), pmErrStr(handle)); + else if ((sts = pmNewContextZone()) < 0) + fprintf(stderr, "%s: cannot set timezone from %s\n" + "pmNewContextZone failed: %s\n", pmProgname, + findsource(dfltHostConn), pmErrStr(sts)); + else + fprintf(stdout, "%s: timezone set to local timezone of host %s\n", + pmProgname, dfltHostConn); + if (handle >= 0) + pmDestroyContext(handle); + } + else if (hostZone) { /* TZ from an archive */ + a = archives; + while (a) { + if (strcmp(dfltHostName, a->hname) == 0) + break; + a = a->next; + } + if (! a) + fprintf(stderr, "%s: no archive supplied for host %s\n", + pmProgname, dfltHostName); + else if ((handle = pmNewContext(PM_CONTEXT_ARCHIVE, a->fname)) < 0) + fprintf(stderr, "%s: cannot set timezone from %s\npmNewContext failed: %s\n", + pmProgname, findsource(dfltHostName), pmErrStr(handle)); + else if ((sts = pmNewContextZone()) < 0) + fprintf(stderr, "%s: cannot set timezone from %s\n" + "pmNewContextZone failed: %s\n", + pmProgname, findsource(dfltHostName), pmErrStr(sts)); + else + fprintf(stdout, "%s: timezone set to local timezone of host %s\n", + pmProgname, dfltHostName); + if (handle >= 0) + pmDestroyContext(handle); + } +} + + +/* convert to canonical units */ +pmUnits +canon(pmUnits in) +{ + static pmUnits out; + + out = in; + out.scaleSpace = PM_SPACE_BYTE; + out.scaleTime = PM_TIME_SEC; + out.scaleCount = 0; + return out; +} + +/* scale factor to canonical pmUnits */ +double +scale(pmUnits in) +{ + double f; + + /* scale space to Mbyte */ + f = pow(1024, in.dimSpace * (in.scaleSpace - PM_SPACE_BYTE)); + + /* scale time to seconds */ + if (in.scaleTime > PM_TIME_SEC) + f *= pow(60, in.dimTime * (in.scaleTime - PM_TIME_SEC)); + else + f *= pow(1000, in.dimTime * (in.scaleTime - PM_TIME_SEC)); + + /* scale events to millions of events */ + f *= pow(10, in.dimCount * in.scaleCount); + + return f; +} + + +/* initialize Metric */ +int /* 1: ok, 0: try again later, -1: fail */ +initMetric(Metric *m) +{ + char *hname = symName(m->hname); + char *mname = symName(m->mname); + char **inames; + int *iids; + int handle; + int ret = 1; + int sts; + int i, j; + + /* set up temporary context */ + if ((handle = newContext(hname)) < 0) + return 0; + + host_state_changed(hname, STATE_RECONN); + + if ((sts = pmLookupName(1, &mname, &m->desc.pmid)) < 0) { + fprintf(stderr, "%s: metric %s not in namespace for %s\n" + "pmLookupName failed: %s\n", + pmProgname, mname, findsource(hname), pmErrStr(sts)); + ret = 0; + goto end; + } + + /* fill in performance metric descriptor */ + if ((sts = pmLookupDesc(m->desc.pmid, &m->desc)) < 0) { + fprintf(stderr, "%s: metric %s not currently available from %s\n" + "pmLookupDesc failed: %s\n", + pmProgname, mname, findsource(hname), pmErrStr(sts)); + ret = 0; + goto end; + } + + if (m->desc.type == PM_TYPE_STRING || + m->desc.type == PM_TYPE_AGGREGATE || + m->desc.type == PM_TYPE_AGGREGATE_STATIC || + m->desc.type == PM_TYPE_EVENT || + m->desc.type == PM_TYPE_HIGHRES_EVENT || + m->desc.type == PM_TYPE_UNKNOWN) { + fprintf(stderr, "%s: metric %s has non-numeric type\n", pmProgname, mname); + ret = -1; + } + else if (m->desc.indom == PM_INDOM_NULL) { + if (m->specinst != 0) { + fprintf(stderr, "%s: metric %s has no instances\n", pmProgname, mname); + ret = -1; + } + else + m->m_idom = 1; + } + else { + /* metric has instances, get full instance profile */ + if (archives) { + if ((sts = pmGetInDomArchive(m->desc.indom, &iids, &inames)) < 0) { + fprintf(stderr, "Metric %s from %s - instance domain not " + "available in archive\npmGetInDomArchive failed: %s\n", + mname, findsource(hname), pmErrStr(sts)); + ret = -1; + } + } + else if ((sts = pmGetInDom(m->desc.indom, &iids, &inames)) < 0) { + fprintf(stderr, "Instance domain for metric %s from %s not (currently) available\n" + "pmGetIndom failed: %s\n", mname, findsource(hname), pmErrStr(sts)); + ret = 0; + } + + if (ret == 1) { /* got instance profile */ + if (m->specinst == 0) { + /* all instances */ + m->iids = iids; + m->m_idom = sts; + m->inames = alloc(m->m_idom*sizeof(char *)); + for (i = 0; i < m->m_idom; i++) { + m->inames[i] = sdup(inames[i]); + } + } + else { + /* selected instances only */ + m->m_idom = 0; + for (i = 0; i < m->specinst; i++) { + /* look for first matching instance name */ + for (j = 0; j < sts; j++) { + if (eqinst(m->inames[i], inames[j])) { + m->iids[i] = iids[j]; + m->m_idom++; + break; + } + } + if (j == sts) { + __pmNotifyErr(LOG_ERR, "metric %s from %s does not " + "(currently) have instance \"%s\"\n", + mname, findsource(hname), m->inames[i]); + m->iids[i] = PM_IN_NULL; + ret = 0; + } + } + if (sts > 0) { + /* + * pmGetInDom or pmGetInDomArchive returned some + * instances above + */ + free(iids); + } + + /* + * if specinst != m_idom, then some not found ... move these + * to the end of the list + */ + for (j = m->specinst-1; j >= 0; j--) { + if (m->iids[j] != PM_IN_NULL) + break; + } + for (i = 0; i < j; i++) { + if (m->iids[i] == PM_IN_NULL) { + /* need to swap */ + char *tp; + tp = m->inames[i]; + m->inames[i] = m->inames[j]; + m->iids[i] = m->iids[j]; + m->inames[j] = tp; + m->iids[j] = PM_IN_NULL; + j--; + } + } + } + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + int numinst; + fprintf(stderr, "initMetric: %s from %s: instance domain specinst=%d\n", + mname, hname, m->specinst); + if (m->m_idom < 1) fprintf(stderr, " %d instances!\n", m->m_idom); + numinst = m->specinst == 0 ? m->m_idom : m->specinst; + for (i = 0; i < numinst; i++) { + fprintf(stderr, " indom[%d]", i); + if (m->iids[i] == PM_IN_NULL) + fprintf(stderr, " ?missing"); + else + fprintf(stderr, " %d", m->iids[i]); + fprintf(stderr, " \"%s\"\n", m->inames[i]); + } + } +#endif + if (sts > 0) { + /* + * pmGetInDom or pmGetInDomArchive returned some instances + * above + */ + free(inames); + } + } + } + + if (ret == 1) { + /* compute conversion factor into canonical units + - non-zero conversion factor flags initialized metric */ + m->conv = scale(m->desc.units); + + /* automatic rate computation */ + if (m->desc.sem == PM_SEM_COUNTER) { + m->vals = (double *) ralloc(m->vals, m->m_idom * sizeof(double)); + for (j = 0; j < m->m_idom; j++) + m->vals[j] = 0; + } + } + +end: + /* destroy temporary context */ + pmDestroyContext(handle); + + /* retry not meaningful for archives */ + if (archives && (ret == 0)) + ret = -1; + + return ret; +} + + +/* reinitialize Metric - only for live host */ +int /* 1: ok, 0: try again later, -1: fail */ +reinitMetric(Metric *m) +{ + char *hname = symName(m->hname); + char *mname = symName(m->mname); + char **inames; + int *iids; + int handle; + int ret = 1; + int sts; + int i, j; + + /* set up temporary context */ + if ((handle = newContext(hname)) < 0) + return 0; + + host_state_changed(hname, STATE_RECONN); + + if ((sts = pmLookupName(1, &mname, &m->desc.pmid)) < 0) { + ret = 0; + goto end; + } + + /* fill in performance metric descriptor */ + if ((sts = pmLookupDesc(m->desc.pmid, &m->desc)) < 0) { + ret = 0; + goto end; + } + + if (m->desc.type == PM_TYPE_STRING || + m->desc.type == PM_TYPE_AGGREGATE || + m->desc.type == PM_TYPE_AGGREGATE_STATIC || + m->desc.type == PM_TYPE_EVENT || + m->desc.type == PM_TYPE_HIGHRES_EVENT || + m->desc.type == PM_TYPE_UNKNOWN) { + fprintf(stderr, "%s: metric %s has non-numeric type\n", pmProgname, mname); + ret = -1; + } + else if (m->desc.indom == PM_INDOM_NULL) { + if (m->specinst != 0) { + fprintf(stderr, "%s: metric %s has no instances\n", pmProgname, mname); + ret = -1; + } + else + m->m_idom = 1; + } + else { + if ((sts = pmGetInDom(m->desc.indom, &iids, &inames)) < 0) { /* full profile */ + ret = 0; + } + else { + if (m->specinst == 0) { + /* all instances */ + m->iids = iids; + m->m_idom = sts; + m->inames = alloc(m->m_idom*sizeof(char *)); + for (i = 0; i < m->m_idom; i++) { + m->inames[i] = sdup(inames[i]); + } + } + else { + /* explicit instance profile */ + m->m_idom = 0; + for (i = 0; i < m->specinst; i++) { + /* look for first matching instance name */ + for (j = 0; j < sts; j++) { + if (eqinst(m->inames[i], inames[j])) { + m->iids[i] = iids[j]; + m->m_idom++; + break; + } + } + if (j == sts) { + m->iids[i] = PM_IN_NULL; + ret = 0; + } + } + if (sts > 0) { + /* + * pmGetInDom or pmGetInDomArchive returned some + * instances above + */ + free(iids); + } + + /* + * if specinst != m_idom, then some not found ... move these + * to the end of the list + */ + for (j = m->specinst-1; j >= 0; j--) { + if (m->iids[j] != PM_IN_NULL) + break; + } + for (i = 0; i < j; i++) { + if (m->iids[i] == PM_IN_NULL) { + /* need to swap */ + char *tp; + tp = m->inames[i]; + m->inames[i] = m->inames[j]; + m->iids[i] = m->iids[j]; + m->inames[j] = tp; + m->iids[j] = PM_IN_NULL; + j--; + } + } + } + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + int numinst; + fprintf(stderr, "reinitMetric: %s from %s: instance domain specinst=%d\n", + mname, hname, m->specinst); + if (m->m_idom < 1) fprintf(stderr, " %d instances!\n", m->m_idom); + if (m->specinst == 0) numinst = m->m_idom; + else numinst = m->specinst; + for (i = 0; i < numinst; i++) { + fprintf(stderr, " indom[%d]", i); + if (m->iids[i] == PM_IN_NULL) + fprintf(stderr, " ?missing"); + else + fprintf(stderr, " %d", m->iids[i]); + fprintf(stderr, " \"%s\"\n", m->inames[i]); + } + } +#endif + if (sts > 0) { + /* + * pmGetInDom or pmGetInDomArchive returned some instances + * above + */ + free(inames); + } + } + } + + if (ret == 1) { + /* compute conversion factor into canonical units + - non-zero conversion factor flags initialized metric */ + m->conv = scale(m->desc.units); + + /* automatic rate computation */ + if (m->desc.sem == PM_SEM_COUNTER) { + m->vals = (double *) ralloc(m->vals, m->m_idom * sizeof(double)); + for (j = 0; j < m->m_idom; j++) + m->vals[j] = 0; + } + } + + if (ret >= 0) { + /* + * re-shape, starting here are working up the expression until + * we reach the top of the tree or the designated metrics + * associated with the node are not the same + */ + Expr *x = m->expr; + while (x) { + /* + * only re-shape expressions that may have set values + */ + if (x->op == CND_FETCH || + x->op == CND_NEG || x->op == CND_ADD || x->op == CND_SUB || + x->op == CND_MUL || x->op == CND_DIV || + x->op == CND_SUM_HOST || x->op == CND_SUM_INST || + x->op == CND_SUM_TIME || + x->op == CND_AVG_HOST || x->op == CND_AVG_INST || + x->op == CND_AVG_TIME || + x->op == CND_MAX_HOST || x->op == CND_MAX_INST || + x->op == CND_MAX_TIME || + x->op == CND_MIN_HOST || x->op == CND_MIN_INST || + x->op == CND_MIN_TIME || + x->op == CND_EQ || x->op == CND_NEQ || + x->op == CND_LT || x->op == CND_LTE || + x->op == CND_GT || x->op == CND_GTE || + x->op == CND_NOT || x->op == CND_AND || x->op == CND_OR || + x->op == CND_RISE || x->op == CND_FALL || + x->op == CND_MATCH || x->op == CND_NOMATCH) { + instFetchExpr(x); + findEval(x); +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "reinitMetric: re-shaped ...\n"); + dumpExpr(x); + } +#endif + } + if (x->parent) { + x = x->parent; + if (x->metrics == m) + continue; + } + break; + } + } + +end: + /* destroy temporary context */ + pmDestroyContext(handle); + + return ret; +} + + +/* put initialised Metric onto fetch list */ +void +bundleMetric(Host *h, Metric *m) +{ + Fetch *f = findFetch(h, m); + if (f == NULL) { + /* + * creating new fetch bundle and pmNewContext failed ... + * not much choice here + */ + waitMetric(m); + } + else + /* usual case */ + findProfile(findFetch(h, m), m); +} + + +/* reconnect attempt to host */ +int +reconnect(Host *h) +{ + Fetch *f; + + f = h->fetches; + while (f) { + if (pmReconnectContext(f->handle) < 0) + return 0; + if (clientid != NULL) + /* re-register client id with pmcd */ + __pmSetClientId(clientid); + f = f->next; + } + return 1; +} + + +/* pragmatics analysis */ +void +pragmatics(Symbol rule, RealTime delta) +{ + Expr *x = symValue(rule); + Task *t; + + if (x->op != NOP) { + t = findTask(delta); + bundle(t, x); + t->nrules++; + t->rules = (Symbol *) ralloc(t->rules, t->nrules * sizeof(Symbol)); + t->rules[t->nrules-1] = symCopy(rule); + perf->eval_expected += (float)1/delta; + } +} + +/* + * find all expressions for a host that has just been marked "down" + * and invalidate them + */ +static void +mark_all(Host *hdown) +{ + Task *t; + Symbol *s; + Metric *m; + Expr *x; + int i; + + for (t = taskq; t != NULL; t = t->next) { + s = t->rules; + for (i = 0; i < t->nrules; i++, s++) { + x = (Expr *)symValue(*s); + for (m = x->metrics; m != NULL; m = m->next) { + if (m->host == hdown) + clobber(x); + } + } + } +} + +/* execute fetches for given Task */ +void +taskFetch(Task *t) +{ + Host *h; + Fetch *f; + Profile *p; + Metric *m; + pmResult *r; + pmValueSet **v; + int i; + int sts; + + /* do all fetches, quick as you can */ + h = t->hosts; + while (h) { + f = h->fetches; + while (f) { + if (f->result) pmFreeResult(f->result); + if (! h->down) { + pmUseContext(f->handle); + if ((sts = pmFetch(f->npmids, f->pmids, &f->result)) < 0) { + if (! archives) { + __pmNotifyErr(LOG_ERR, "pmFetch from %s failed: %s\n", + symName(f->host->name), pmErrStr(sts)); + host_state_changed(symName(f->host->name), STATE_LOSTCONN); + h->down = 1; + mark_all(h); + } + f->result = NULL; + } + } + else + f->result = NULL; + f = f->next; + } + h = h->next; + } + + /* sort and distribute pmValueSets to requesting Metrics */ + h = t->hosts; + while (h) { + if (! h->down) { + f = h->fetches; + while (f && (r = f->result)) { + /* sort all vlists in result r */ + v = r->vset; + for (i = 0; i < r->numpmid; i++) { + if ((*v)->numval > 0) { + qsort((*v)->vlist, (size_t)(*v)->numval, + sizeof(pmValue), compair); + } + v++; + } + + /* distribute pmValueSets to Metrics */ + p = f->profiles; + while (p) { + m = p->metrics; + while (m) { + for (i = 0; i < r->numpmid; i++) { + if (m->desc.pmid == r->vset[i]->pmid) { + if (r->vset[i]->numval > 0) { + m->vset = r->vset[i]; + m->stamp = realize(r->timestamp); + } + break; + } + } + m = m->next; + } + p = p->next; + } + f = f->next; + } + } + h = h->next; + } +} + + +/* send pmDescriptors for all expressions in given task */ +void +sendDescs(Task *task) +{ + Symbol *s; + int i; + + s = task->rules; + for (i = 0; i < task->nrules; i++) { + sendDesc(symValue(*s), task->rslt->vset[i]); + s++; + } +} + + +/* convert Expr value to pmValueSet value */ +void +fillVSet(Expr *x, pmValueSet *vset) +{ + if (x->valid > 0) { + if (finite(*((double *)x->ring))) { /* copy value */ + vset->numval = 1; + memcpy(&vset->vlist[0].value.pval->vbuf, x->ring, sizeof(double)); + } + else /* value not representable */ + vset->numval = 0; + } + else { /* value not available */ + vset->numval = PM_ERR_VALUE; + } +} diff --git a/src/pmie/src/pragmatics.h b/src/pmie/src/pragmatics.h new file mode 100644 index 0000000..bee5f88 --- /dev/null +++ b/src/pmie/src/pragmatics.h @@ -0,0 +1,103 @@ +/*********************************************************************** + * pragmatics.h - inference engine pragmatics analysis + * + * The analysis of how to organize the fetching of metrics (pragmatics) + * and other parts of the inference engine that are particularly + * sensitive to details of the performance metrics API are kept here. + *********************************************************************** + * + * 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 PRAG_H +#define PRAG_H + +#include "pmapi.h" + +/* report where PCP data is coming from */ +char *findsource(char *); + +/* initialize performance metrics API */ +void pmcsInit(void); + +/* juggle contexts */ +int newContext(char *); + +/* initialize access to archive */ +int initArchive(Archive *); + +/* initialize timezone */ +void zoneInit(void); + +/* convert to canonical units */ +pmUnits canon(pmUnits); + +/* scale factor to canonical pmUnits */ +double scale(pmUnits); + +/* initialize Metric */ +int initMetric(Metric *); + +/* reinitialize Metric */ +int reinitMetric(Metric *); + +/* put initialiaed Metric onto fetch list */ +void bundleMetric(Host *, Metric *); + +/* reconnect attempt to host */ +int reconnect(Host *); + +/* pragmatics analysis */ +void pragmatics(Symbol, RealTime); + +/* execute fetches for given Task */ +void taskFetch(Task *); + +/* convert Expr value to pmValueSet value */ +void fillVSet(Expr *, pmValueSet *); + +/* send pmDescriptors for all expressions in given task */ +void sendDescs(Task *); + +/* put Metric onto wait list */ +void waitMetric(Metric *); + +/* remove Metric from wait list */ +void unwaitMetric(Metric *); + +/* check that pmUnits dimensions are equal */ +#define dimeq(x, y) (((x).dimSpace == (y).dimSpace) && \ + ((x).dimTime == (y).dimTime) && \ + ((x).dimCount == (y).dimCount)) + +/* check equality of two pmUnits */ +#define unieq(x, y) (((x).dimSpace == (y).dimSpace) && \ + ((x).dimTime == (y).dimTime) && \ + ((x).dimCount == (y).dimCount) && \ + ((x).scaleSpace == (y).scaleSpace) && \ + ((x).scaleTime == (y).scaleTime) && \ + ((x).scaleCount == (y).scaleCount)) + +/* for initialization of pmUnits struct */ +extern pmUnits noUnits; +extern pmUnits countUnits; + +/* flag processes spawned */ +extern int need_wait; + +#endif /* PRAG_H */ + + diff --git a/src/pmie/src/show.c b/src/pmie/src/show.c new file mode 100644 index 0000000..f879679 --- /dev/null +++ b/src/pmie/src/show.c @@ -0,0 +1,1104 @@ +/*********************************************************************** + * show.c - display expressions and their values + *********************************************************************** + * + * 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. + * + * 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 <math.h> +#include <ctype.h> +#include <assert.h> +#include "show.h" +#include "impl.h" +#include "dstruct.h" +#include "lexicon.h" +#include "pragmatics.h" +#if defined(HAVE_IEEEFP_H) +#include <ieeefp.h> +#endif + +/*********************************************************************** + * local declarations + ***********************************************************************/ + +static struct { + int op; + char *str; +} opstr[] = { + { RULE, "->" }, + { CND_FETCH, "<fetch node>" }, + { CND_DELAY, "<delay node>" }, + { CND_RATE, "rate" }, + { CND_NEG, "-" }, + { CND_ADD, "+" }, + { CND_SUB, "-" }, + { CND_MUL, "*" }, + { CND_DIV, "/" }, +/* aggregation */ + { CND_SUM_HOST, "sum_host" }, + { CND_SUM_INST, "sum_inst" }, + { CND_SUM_TIME, "sum_sample" }, + { CND_AVG_HOST, "avg_host" }, + { CND_AVG_INST, "avg_inst" }, + { CND_AVG_TIME, "avg_sample" }, + { CND_MAX_HOST, "max_host" }, + { CND_MAX_INST, "max_inst" }, + { CND_MAX_TIME, "max_sample" }, + { CND_MIN_HOST, "min_host" }, + { CND_MIN_INST, "min_inst" }, + { CND_MIN_TIME, "min_sample" }, +/* relational */ + { CND_EQ, "==" }, + { CND_NEQ, "!=" }, + { CND_LT, "<" }, + { CND_LTE, "<=" }, + { CND_GT, ">" }, + { CND_GTE, ">=" }, +/* boolean */ + { CND_NOT, "!" }, + { CND_RISE, "rising" }, + { CND_FALL, "falling" }, + { CND_AND, "&&" }, + { CND_OR, "||" }, + { CND_MATCH, "match_inst" }, + { CND_NOMATCH, "nomatch_inst" }, + { CND_RULESET, "ruleset" }, + { CND_OTHER, "other" }, +/* quantification */ + { CND_ALL_HOST, "all_host" }, + { CND_ALL_INST, "all_inst" }, + { CND_ALL_TIME, "all_sample" }, + { CND_SOME_HOST, "some_host" }, + { CND_SOME_INST, "some_inst" }, + { CND_SOME_TIME, "some_sample" }, + { CND_PCNT_HOST, "pcnt_host" }, + { CND_PCNT_INST, "pcnt_inst" }, + { CND_PCNT_TIME, "pcnt_sample" }, + { CND_COUNT_HOST, "count_host" }, + { CND_COUNT_INST, "count_inst" }, + { CND_COUNT_TIME, "count_sample" }, + { ACT_SEQ, "&" }, + { ACT_ALT, "|" }, + { ACT_SHELL, "shell" }, + { ACT_ALARM, "alarm" }, + { ACT_SYSLOG, "syslog" }, + { ACT_PRINT, "print" }, + { ACT_STOMP, "stomp" }, + { ACT_ARG, "<action arg node>" }, + { NOP, "<nop node>" }, + { OP_VAR, "<op_var node>" }, +}; + +static int numopstr = sizeof(opstr) / sizeof(opstr[0]); + +/*********************************************************************** + * local utility functions + ***********************************************************************/ + +/* Concatenate string1 to existing string2 whose original length is given. */ +static size_t /* new length of *string2 */ +concat(char *string1, size_t pos, char **string2) +{ + size_t slen; + size_t tlen; + char *cat; + char *dog; + + if ((slen = strlen(string1)) == 0) + return pos; + tlen = pos + slen; + cat = (char *) ralloc(*string2, tlen + 1); + dog = cat + pos; + strcpy(dog, string1); + dog += slen; + *dog = '\0'; + + *string2 = cat; + return tlen; +} + + +/*********************************************************************** + * host and instance names + ***********************************************************************/ + +/* Return host and instance name for nth value in expression *x */ +static int +lookupHostInst(Expr *x, int nth, char **host, char **inst) +{ + Metric *m = NULL; + int mi; + int sts = 0; + int pick = -1; + int matchaggr = 0; + int aggrop = NOP; + double *aggrval = NULL; +#if PCP_DEBUG + static Expr *lastx = NULL; + int dbg_dump = 0; +#endif + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + if (x != lastx) { + fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...)\n", x, nth); + lastx = x; + dbg_dump = 1; + } + } +#endif + if (x->op == CND_MIN_HOST || x->op == CND_MAX_HOST || + x->op == CND_MIN_INST || x->op == CND_MAX_INST || + x->op == CND_MIN_TIME || x->op == CND_MAX_TIME) { + /* + * extrema operators ... value is here, but the host, instance, sample + * context is in the child expression ... go one level deeper and try + * to match the value + */ + aggrop = x->op; + aggrval = (double *)x->smpls[0].ptr; + matchaggr = 1; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "lookupHostInst look for extrema val=%f @ " PRINTF_P_PFX "%p\n", *aggrval, x); + } + x = x->arg1; +#endif + } + + /* check for no host and instance available e.g. constant expression */ + if ((x->e_idom <= 0 && x->hdom <= 0) || ! x->metrics) { + *host = NULL; + *inst = NULL; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...) -> %%h and %%i undefined\n", x, nth); + } +#endif + return sts; + } + + /* find Metric containing the nth instance */ + if (matchaggr == 0) { + pick = nth; + mi = 0; + for (;;) { + m = &x->metrics[mi]; +#if PCP_DEBUG + if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) { + fprintf(stderr, "lookupHostInst: metrics[%d]\n", mi); + dumpMetric(m); + } +#endif + if (pick < m->m_idom) + break; + if (m->m_idom > 0) + pick -= m->m_idom; + mi++; + } + } + else { + if (aggrop == CND_MIN_HOST || aggrop == CND_MAX_HOST) { + int k; +#if PCP_DEBUG + if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) { + fprintf(stderr, "lookupHostInst [extrema_host]:\n"); + } +#endif + for (k = 0; k < x->tspan; k++) { +#if DESPERATE + fprintf(stderr, "smpls[0][%d]=%g\n", k, *((double *)x->smpls[0].ptr+k)); +#endif + if (*aggrval == *((double *)x->smpls[0].ptr+k)) { + m = &x->metrics[k]; + goto done; + } + } + fprintf(stderr, "Internal error: LookupHostInst: %s\n", opStrings(aggrop)); + } + else if (aggrop == CND_MIN_INST || aggrop == CND_MAX_INST) { + int k; + for (k = 0; k < x->tspan; k++) { +#if DESPERATE + fprintf(stderr, "smpls[0][%d]=%g\n", k, *((double *)x->smpls[0].ptr+k)); +#endif + if (*aggrval == *((double *)x->smpls[0].ptr+k)) { + pick = k; + m = &x->metrics[0]; +#if PCP_DEBUG + if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) { + fprintf(stderr, "lookupHostInst [extrema_inst]:\n"); + dumpMetric(m); + } +#endif + goto done; + } + } + fprintf(stderr, "Internal error: LookupHostInst: %s\n", opStrings(aggrop)); + } + else if (aggrop == CND_MIN_TIME || aggrop == CND_MAX_TIME) { + int k; + for (k = 0; k < x->nsmpls; k++) { +#if DESPERATE + fprintf(stderr, "smpls[%d][0]=%g\n", k, *((double *)x->smpls[k].ptr)); +#endif + if (*aggrval == *((double *)x->smpls[k].ptr)) { + pick = nth; + m = &x->metrics[0]; +#if PCP_DEBUG + if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) { + fprintf(stderr, "lookupHostInst [extrema_sample]:\n"); + dumpMetric(m); + } +#endif + goto done; + } + } + fprintf(stderr, "Internal error: LookupHostInst: %s\n", opStrings(aggrop)); + } + } + +done: + /* host and instance names */ + if (m == NULL) { + *host = NULL; + *inst = NULL; + } + else { + *host = symName(m->hname); + sts++; + if (pick >= 0 && x->e_idom > 0 && m->inames) { + *inst = m->inames[pick]; + sts++; + } + else + *inst = NULL; + } + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...) -> sts=%d %%h=%s %%i=%s\n", + x, nth, sts, *host, *inst == NULL ? "undefined" : *inst); + } +#endif + + return sts; +} + + +/*********************************************************************** + * expression value + ***********************************************************************/ + +#define BOOLEAN_SPACE 8 + +static size_t +showBoolean(Expr *x, int nth, size_t length, char **string) +{ + int smpl; + size_t tlen; + char *cat; + char *dog; + int val; + + tlen = length + (x->nsmpls * BOOLEAN_SPACE); + cat = (char *)ralloc(*string, tlen + 1); + dog = cat + length; + for (smpl = 0; smpl < x->nsmpls; smpl++) { + if (smpl > 0) { + strcpy(dog, " "); + dog += 1; + } + + if (x->valid == 0) { + strncpy(dog, "unknown", BOOLEAN_SPACE); + dog += strlen("unknown"); + continue; + } + + val = *((char *)x->smpls[smpl].ptr + nth); + if (val == B_FALSE) { + strncpy(dog, "false", BOOLEAN_SPACE); + dog += strlen("false"); + } + else if (val == B_TRUE) { + strncpy(dog, "true", BOOLEAN_SPACE); + dog += strlen("true"); + } + else if (val == B_UNKNOWN) { + strncpy(dog, "unknown", BOOLEAN_SPACE); + dog += strlen("unknown"); + } + else { + sprintf(dog, "0x%02x?", val & 0xff); + dog += 5; + } + } + *dog = '\0'; + + *string = cat; + return dog - cat; +} + + +static size_t +showString(Expr *x, size_t length, char **string) +{ + size_t slen; + size_t tlen; + char *cat; + char *dog; + + slen = strlen((char *)x->smpls[0].ptr); + tlen = length + slen + 2; + cat = (char *)ralloc(*string, tlen + 1); + dog = cat + length; + *dog++ = '"'; + strcpy(dog, (char *)x->smpls[0].ptr); + dog += slen; + *dog++ = '"'; + *dog = '\0'; + + *string = cat; + return tlen; +} + +#define DBL_SPACE 24 + +static size_t +showNum(Expr *x, int nth, size_t length, char **string) +{ + int smpl; + size_t tlen; + char *cat; + char *dog; + char *fmt; + double v; + double abs_v; + int sts; + + tlen = length + (x->nsmpls * DBL_SPACE); + cat = (char *)ralloc(*string, tlen + 1); + dog = cat + length; + for (smpl = 0; smpl < x->nsmpls; smpl++) { + int noval = 0; + if (smpl > 0) { + strcpy(dog, " "); + dog++; + } + if (x->valid <= smpl) + noval = 1; + else { +#ifdef HAVE_FPCLASSIFY + noval = fpclassify(*((double *)x->smpls[smpl].ptr + nth)) == FP_NAN; +#else +#ifdef HAVE_ISNAN + noval = isnan(*((double *)x->smpls[smpl].ptr + nth)); +#endif +#endif + } + if (noval) { + if (x->sem == SEM_BOOLEAN) { + strcpy(dog, "unknown"); + dog += strlen("unknown"); + } + else { + strcpy(dog, "?"); + dog++; + } + } + else { + v = *((double *)x->smpls[smpl].ptr+nth); + if (v == (int)v) + sts = sprintf(dog, "%d", (int)v); + else { + abs_v = v < 0 ? -v : v; + if (abs_v < 0.5) + fmt = "%g"; + else if (abs_v < 5) + fmt = "%.2f"; + else if (abs_v < 50) + fmt = "%.1f"; + else + fmt = "%.0f"; + sts = sprintf(dog, fmt, v); + } + if (sts > 0) + dog += sts; + else { + strcpy(dog, "!"); + dog += 1; + } + } + } + *dog = '\0'; + + *string = cat; + return dog - cat; +} + +static char * +showConst(Expr *x) +{ + char *string = NULL; + size_t length = 0; + int i; + int first = 1; + + /* construct string representation */ + if (x->nvals > 0) { + for (i = 0; i < x->tspan; i++) { + if (first) + first = 0; + else + length = concat(" ", length, &string); + if (x->sem == SEM_BOOLEAN) + length = showBoolean(x, i, length, &string); + else if (x->sem == SEM_REGEX) { + /* regex is compiled, cannot recover original string */ + length = concat("/<regex>/", length, &string); + } + else if (x->sem == SEM_CHAR) { + length = showString(x, length, &string); + /* tspan is string length, not an iterator in this case */ + break; + } + else + length = showNum(x, i, length, &string); + } + } + return string; +} + + + +/*********************************************************************** + * expression syntax + ***********************************************************************/ + +static void +showSyn(FILE *f, Expr *x) +{ + char *s; + char *c; + Metric *m; + char **n; + int i; + int paren; + + if (x->op == NOP) { + /* constant */ + s = showConst(x); + if (s) { + c = s; + while(isspace((int)*c)) + c++; + fputs(c, f); + free(s); + } + } + else if ((x->op == CND_FETCH) || (x->op == CND_DELAY)) { + /* fetch expression (perhaps with delay) */ + m = x->metrics; + fprintf(f, "%s", symName(m->mname)); + for (i = 0; i < x->hdom; i++) { + fprintf(f, " :%s", symName(m->hname)); + m++; + } + m = x->metrics; + if (m->inames) { + n = m->inames; + for (i = 0; i < m->m_idom; i++) { + fprintf(f, " #%s", *n); + n++; + } + } + if (x->op == CND_FETCH) { + if (x->tdom > 1) + fprintf(f, " @0..%d", x->tdom - 1); + } + else { + if (x->tdom == x->arg1->tdom - 1) fprintf(f, " @%d", x->tdom); + else fprintf(f, " @%d..%d", x->tdom, x->tdom + x->arg1->tdom - 1); + } + } + else if (x->arg1 && x->arg2) { + /* binary operator */ + if (x->op == ACT_SHELL || x->op == ACT_ALARM || x->op == ACT_PRINT || + x->op == ACT_STOMP) { + fputs(opStrings(x->op), f); + fputc(' ', f); + showSyn(f, x->arg2); + fputc(' ', f); + showSyn(f, x->arg1); + } + else if (x->op == ACT_ARG && x->parent->op == ACT_SYSLOG) { + int *ip; + char *cp; + ip = x->arg2->ring; + cp = (char *)&ip[1]; + fprintf(f, "[level=%d tag=\"%s\"]", *ip, cp); + fputc(' ', f); + showSyn(f, x->arg1); + } + else if (x->op == CND_PCNT_HOST || x->op == CND_PCNT_INST || x->op == CND_PCNT_TIME) { + int pcnt; + fputs(opStrings(x->op), f); + fputc(' ', f); + /* + * used to showSyn(f, x->arg2) here, but formatting is a little + * better if we punt on there being a single double representation + * of the % value at the end of arg2 + */ + pcnt = (int)(*((double *)x->arg2->smpls[0].ptr)*100+0.5); + fprintf(f, "%d%%", pcnt); + fputc(' ', f); + if (x->arg1->op == NOP || x->arg1->op == CND_DELAY || x->arg1->op == CND_FETCH) + showSyn(f, x->arg1); + else { + fputc('(', f); + showSyn(f, x->arg1); + fputc(')', f); + } + } + else if (x->op == CND_MATCH || x->op == CND_NOMATCH) { + fputs(opStrings(x->op), f); + fputc(' ', f); + showSyn(f, x->arg2); + fputc(' ', f); + fputc('(', f); + showSyn(f, x->arg1); + fputc(')', f); + } + else { + paren = 1 - + (x->arg1->op == NOP || x->arg1->op == CND_DELAY || + x->arg1->op == CND_FETCH || x->arg1->op == CND_RATE || + x->arg1->op == CND_SUM_HOST || x->arg1->op == CND_SUM_INST || + x->arg1->op == CND_SUM_TIME || x->arg1->op == CND_AVG_HOST || + x->arg1->op == CND_AVG_INST || x->arg1->op == CND_AVG_TIME || + x->arg1->op == CND_MAX_HOST || x->arg1->op == CND_MAX_INST || + x->arg1->op == CND_MAX_TIME || x->arg1->op == CND_MIN_HOST || + x->arg1->op == CND_MIN_INST || x->arg1->op == CND_MIN_TIME || + x->arg1->op == CND_COUNT_HOST || x->arg1->op == CND_COUNT_INST || + x->arg1->op == CND_COUNT_TIME || x->arg1->op == CND_RATE || + x->op == RULE); + if (paren) + fputc('(', f); + showSyn(f, x->arg1); + if (paren) + fputc(')', f); + fputc(' ', f); + fputs(opStrings(x->op), f); + fputc(' ', f); + paren = 1 - + (x->arg2->op == NOP || x->arg2->op == CND_DELAY || + x->arg2->op == CND_FETCH || x->arg2->op == CND_RATE || + x->arg2->op == CND_SUM_HOST || x->arg2->op == CND_SUM_INST || + x->arg2->op == CND_SUM_TIME || x->arg2->op == CND_AVG_HOST || + x->arg2->op == CND_AVG_INST || x->arg2->op == CND_AVG_TIME || + x->arg2->op == CND_MAX_HOST || x->arg2->op == CND_MAX_INST || + x->arg2->op == CND_MAX_TIME || x->arg2->op == CND_MIN_HOST || + x->arg2->op == CND_MIN_INST || x->arg2->op == CND_MIN_TIME || + x->arg2->op == CND_COUNT_HOST || x->arg2->op == CND_COUNT_INST || + x->arg2->op == CND_COUNT_TIME || x->arg2->op == CND_RATE || + x->op == RULE); + if (paren) + fputc('(', f); + showSyn(f, x->arg2); + if (paren) + fputc(')', f); + } + } + else { + /* unary operator */ + assert(x->arg1 != NULL); + if (x->op == ACT_ARG) { + /* parameters for an action */ + Expr *y = x->arg1; + while (y != NULL) { + if (y != x->arg1) + fputc(' ', f); + showSyn(f, y); + // fprintf(f, "\"%s\"", (char *)y->smpls[0].ptr); + y = y->arg1; + } + } + else { + fputs(opStrings(x->op), f); + fputc(' ', f); + paren = 1 - + (x->arg1->op == ACT_SEQ || x->arg1->op == ACT_ALT || + x->op == ACT_SHELL || x->op == ACT_ALARM || + x->op == ACT_SYSLOG || x->op == ACT_PRINT || + x->op == ACT_STOMP || x->op == CND_DELAY); + if (paren) + fputc('(', f); + showSyn(f, x->arg1); + if (paren) + fputc(')', f); + } + } +} + +/* + * recursive descent to find a conjunct from the root of + * the expression that has associated metrics (not constants) + */ +static Expr * +findMetrics(Expr *y) +{ + Expr *z; + + if (y == NULL) return NULL; + if (y->metrics) return y; /* success */ + + /* give up if not a conjunct */ + if (y->op != CND_AND) return NULL; + + /* recurse left and then right */ + z = findMetrics(y->arg1); + if (z != NULL) return z; + return findMetrics(y->arg2); +} + +/*********************************************************************** + * satisfying bindings and values + ***********************************************************************/ + +/* Find sub-expression that reveals host and instance bindings + that satisfy the given expression *x. */ +static Expr * +findBindings(Expr *x) +{ +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "call findBindings(x=" PRINTF_P_PFX "%p)\n", x); + } +#endif + + if (x->metrics == NULL) { + /* + * this Expr node has no metrics (involves only constants) + * ... try and find a conjunct at the top level that has + * associated metrics + */ + Expr *y = findMetrics(x->arg1); + if (y != NULL) x = y; + } + while (x->metrics && (x->e_idom <= 0 || x->hdom <= 0)) { + if (x->op == CND_SUM_HOST || x->op == CND_SUM_INST || x->op == CND_SUM_TIME || + x->op == CND_AVG_HOST || x->op == CND_AVG_INST || x->op == CND_AVG_TIME || + x->op == CND_MAX_HOST || x->op == CND_MAX_INST || x->op == CND_MAX_TIME || + x->op == CND_MIN_HOST || x->op == CND_MIN_INST || x->op == CND_MIN_TIME || + x->op == CND_COUNT_HOST || x->op == CND_COUNT_INST || x->op == CND_COUNT_TIME) { + /* + * don't descend below an aggregation operator with a singular + * value, ... value you seek is right here + */ +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "findBindings: found %s @ x=" PRINTF_P_PFX "%p\n", opStrings(x->op), x); + } +#endif + break; + } + if (x->arg1 && x->metrics == x->arg1->metrics) { + x = x->arg1; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "findBindings: try x->arg1=" PRINTF_P_PFX "%p\n", x); + } +#endif + } + else if (x->arg2) { + x = x->arg2; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "findBindings: try x->arg2=" PRINTF_P_PFX "%p\n", x); + } +#endif + } + else + break; + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "findBindings finish @ " PRINTF_P_PFX "%p\n", x); + dumpTree(x); + } +#endif + return x; +} + + +/* Find sub-expression that reveals the values that satisfy the + given expression *x. */ +static Expr * +findValues(Expr *x) +{ +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "call findValues(x=" PRINTF_P_PFX "%p)\n", x); + } +#endif + while (x->sem == SEM_BOOLEAN && x->metrics) { + if (x->metrics == x->arg1->metrics) { + x = x->arg1; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "findValues: try x->arg1=" PRINTF_P_PFX "%p\n", x); + } +#endif + } + else { + x = x->arg2; +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "findValues: try x->arg2=" PRINTF_P_PFX "%p\n", x); + } +#endif + } + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "findValues finish @ " PRINTF_P_PFX "%p\n", x); + dumpTree(x); + } +#endif + return x; +} + + +/*********************************************************************** + * format string + ***********************************************************************/ + +/* Locate next %h, %i or %v in format string. */ +static int /* 0 -> not found, 1 -> host, 2 -> inst, 3 -> value */ +findFormat(char *format, char **pos) +{ + for (;;) { + if (*format == '\0') + return 0; + if (*format == '%') { + switch (*(format + 1)) { + case 'h': + *pos = format; + return 1; + case 'i': + *pos = format; + return 2; + case 'v': + *pos = format; + return 3; + } + } + format++; + } + +} + + +/*********************************************************************** + * exported functions + ***********************************************************************/ + +void +showSyntax(FILE *f, Symbol s) +{ + char *name = symName(s); + Expr *x = symValue(s); + + fprintf(f, "%s =\n", name); + showSyn(f, x); + fprintf(f, ";\n\n"); +} + + +void +showSubsyntax(FILE *f, Symbol s) +{ + char *name = symName(s); + Expr *x = symValue(s); + Expr *x1; + Expr *x2; + + fprintf(f, "%s (subexpression for %s, %s and %s bindings) =\n", + name, "%h", "%i", "%v"); + x1 = findBindings(x); + x2 = findValues(x1); + showSyn(f, x2); + fprintf(f, "\n\n"); +} + + +/* Print value of expression */ +void +showValue(FILE *f, Expr *x) +{ + char *string = NULL; + + string = showConst(x); + if (string) { + fputs(string, f); + free(string); + } + else + fputs("?", f); +} + + +/* Print value of expression together with any host and instance bindings */ +void +showAnnotatedValue(FILE *f, Expr *x) +{ + char *string = NULL; + size_t length = 0; + char *host; + char *inst; + int i; + + /* no annotation possible */ + if ((x->e_idom <= 0 && x->hdom <= 0) || + x->sem == SEM_CHAR || + x->metrics == NULL || + x->valid == 0) { + showValue(f, x); + return; + } + + /* construct string representation */ + for (i = 0; i < x->tspan; i++) { + length = concat("\n ", length, &string); + lookupHostInst(x, i, &host, &inst); + length = concat(host, length, &string); + if (inst) { + length = concat(": [", length, &string); + length = concat(inst, length, &string); + length = concat("] ", length, &string); + } + else + length = concat(": ", length, &string); + if (x->sem == SEM_BOOLEAN) + length = showBoolean(x, i, length, &string); + else /* numeric value */ + length = showNum(x, i, length, &string); + } + + /* print string representation */ + if (string) { + fputs(string, f); + free(string); + } +} + + +void +showTime(FILE *f, RealTime rt) +{ + time_t t = (time_t)rt; + char bfr[26]; + + pmCtime(&t, bfr); + bfr[24] = '\0'; + fprintf(f, "%s", bfr); +} + + +void +showFullTime(FILE *f, RealTime rt) +{ + time_t t = (time_t)rt; + char bfr[26]; + + pmCtime(&t, bfr); + bfr[24] = '\0'; + fprintf(f, "%s.%06d", bfr, (int)((rt-t)*1000000)); +} + + +void +showSatisfyingValue(FILE *f, Expr *x) +{ + char *string = NULL; + size_t length = 0; + char *host; + char *inst; + int i; + Expr *x1; + Expr *x2; + + /* no satisfying values possible */ + if (x->metrics == NULL || x->valid == 0) { + showValue(f, x); + return; + } + + if (x->sem != SEM_BOOLEAN) { + showAnnotatedValue(f, x); + return; + } + + x1 = findBindings(x); + x2 = findValues(x1); + if (!x1->valid) { + /* + * subexpression for %h, %i and %v is not valid but rule is + * true, return string without substitution ... rare case + * for <bad-or-not evaluated expr> || <true expr> rule + */ + concat(" <no bindings available>", length, &string); + goto done; + } + + /* construct string representation */ + for (i = 0; i < x1->tspan; i++) { + if ((x1->sem == SEM_BOOLEAN && *((char *)x1->smpls[0].ptr + i) == B_TRUE) + || (x1->sem != SEM_BOOLEAN && x1->sem != SEM_UNKNOWN)) { + length = concat("\n ", length, &string); + lookupHostInst(x1, i, &host, &inst); + length = concat(host, length, &string); + if (inst) { + length = concat(": [", length, &string); + length = concat(inst, length, &string); + length = concat("] ", length, &string); + } + else + length = concat(": ", length, &string); + if (x2->sem == SEM_BOOLEAN) + length = showBoolean(x2, i, length, &string); + else /* numeric value */ + length = showNum(x2, i, length, &string); + } + } + +done: + /* print string representation */ + if (string) { + fputs(string, f); + free(string); + } +} + + +/* + * Instantiate format string for each satisfying binding and value + * of the current rule ... enumerate and insert %h, %v and %v values + * + * WARNING: This is not thread safe, it dinks with the format string. + */ +size_t /* new length of string */ +formatSatisfyingValue(char *format, size_t length, char **string) +{ + char *host; + char *inst; + char *first; + char *prev; + char *next; + int i; + Expr *x1; + Expr *x2; + int sts1; + int sts2; + + /* no formatting present? */ + if ((sts1 = findFormat(format, &first)) == 0) + return concat(format, length, string); + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "formatSatisfyingValue: curr=" PRINTF_P_PFX "%p\n", curr); + dumpExpr(curr); + } +#endif + x1 = findBindings(curr); + x2 = findValues(x1); + if (!x1->valid) + /* + * subexpression for %h, %i and %v is not valid but rule is + * true, return string without substitution ... rare case + * for <bad-or-not evaluated expr> || <true expr> rule + */ + return concat(format, length, string); + + for (i = 0; i < x1->tspan; i++) { + if ((x1->sem == SEM_BOOLEAN && *((char *)x1->smpls[0].ptr + i) == B_TRUE) + || (x1->sem != SEM_BOOLEAN && x1->sem != SEM_UNKNOWN)) { + prev = format; + next = first; + sts2 = sts1; + lookupHostInst(x1, i, &host, &inst); + do { + *next = '\0'; + length = concat(prev, length, string); + *next = '%'; + + switch (sts2) { + case 1: + if (host) + length = concat(host, length, string); + else + length = concat("<%h undefined>", length, string); + break; + case 2: + if (inst) + length = concat(inst, length, string); + else + length = concat("<%i undefined>", length, string); + break; + case 3: + if (x2->sem == SEM_BOOLEAN) + length = showBoolean(x2, i, length, string); + else /* numeric value */ + length = showNum(x2, i, length, string); + break; + } + prev = next + 2; + } while ((sts2 = findFormat(prev, &next))); + length = concat(prev, length, string); + } + } + + return length; +} + +char * +opStrings(int op) +{ + int i; + /* + * sizing of "eh" is a bit tricky ... + * XXXXXXXXX is the number of digits in the largest possible value + * for "op", to handle the default "<unknown op %d>" case, but also + * "eh" must be long enough to accommodate the longest string from + * opstr[i].str ... currently "<action arg node>" + */ + static char *eh = "<unknown op XXXXXXXXX>"; + + for (i = 0; i < numopstr; i++) { + if (opstr[i].op == op) + break; + } + + if (i < numopstr) + return opstr[i].str; + else { + sprintf(eh, "<unknown op %d>", op); + return eh; + } +} diff --git a/src/pmie/src/show.h b/src/pmie/src/show.h new file mode 100644 index 0000000..83341ac --- /dev/null +++ b/src/pmie/src/show.h @@ -0,0 +1,32 @@ +/*********************************************************************** + * show.h - output syntax and values + *********************************************************************** + * + * 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 "dstruct.h" + +char *opStrings(int); + +void showSyntax(FILE *,Symbol); +void showSubsyntax(FILE *, Symbol); +void showValue(FILE *, Expr *); +void showAnnotatedValue(FILE *, Expr *); +void showSatisfyingValue(FILE *, Expr *); +void showTime(FILE *, RealTime); +void showFullTime(FILE *, RealTime); +size_t formatSatisfyingValue(char *, size_t, char **); diff --git a/src/pmie/src/stats.h b/src/pmie/src/stats.h new file mode 100644 index 0000000..d3d819f --- /dev/null +++ b/src/pmie/src/stats.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1999 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. + */ +#ifndef STATS_H +#define STATS_H + +#include <sys/types.h> +#include <sys/param.h> + +/* subdir nested under PCP_TMP_DIR */ +#define PMIE_SUBDIR "pmie" + +/* pmie performance instrumentation */ +typedef struct { + char config[MAXPATHLEN+1]; + char logfile[MAXPATHLEN+1]; + char defaultfqdn[MAXHOSTNAMELEN+1]; + float eval_expected; /* pmcd.pmie.eval.expected */ + unsigned int numrules; /* pmcd.pmie.numrules */ + unsigned int actions; /* pmcd.pmie.actions */ + unsigned int eval_true; /* pmcd.pmie.eval.true */ + unsigned int eval_false; /* pmcd.pmie.eval.false */ + unsigned int eval_unknown; /* pmcd.pmie.eval.unknown */ + unsigned int eval_actual; /* pmcd.pmie.eval.actual */ + unsigned int version; +} pmiestats_t; + +#endif /* STATS_H */ diff --git a/src/pmie/src/stomp.c b/src/pmie/src/stomp.c new file mode 100644 index 0000000..78a21b8 --- /dev/null +++ b/src/pmie/src/stomp.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2006 Aconex. 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 "pmapi.h" +#include "impl.h" + +static int stomp_connect(const char *hostname, int port); +static void stomp_disconnect(void); + +int stomping; +char *stompfile; +extern int verbose; + +static int fd = -1; +static int port = -1; +static int timeout = 2; /* default 2 sec to timeout JMS server ACKs */ +static char *hostname; +static char *username; +static char *passcode; +static char *topic; /* JMS "topic" for pmie messages */ +static char pmietopic[] = "PMIE"; /* default JMS "topic" for pmie */ + +static char buffer[4096]; + +static int stomp_connect(const char *hostname, int port) +{ + __pmSockAddr *myaddr; + __pmHostEnt *servinfo; + void *enumIx; + struct timeval tv; + struct timeval *ptv; + __pmFdSet wfds; + int ret; + int flags = 0; + + if ((servinfo = __pmGetAddrInfo(hostname)) == NULL) + return -1; + + fd = -1; + enumIx = NULL; + for (myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx); + myaddr != NULL; + myaddr = __pmHostEntGetSockAddr(servinfo, &enumIx)) { + /* Create a socket */ + if (__pmSockAddrIsInet(myaddr)) + fd = __pmCreateSocket(); + else if (__pmSockAddrIsIPv6(myaddr)) + fd = __pmCreateIPv6Socket(); + else + continue; + if (fd < 0) { + __pmSockAddrFree(myaddr); + continue; /* Try the next address */ + } + + /* Attempt to connect */ + flags = __pmConnectTo(fd, myaddr, port); + __pmSockAddrFree(myaddr); + + if (flags < 0) { + /* + * Mark failure in case we fall out the end of the loop + * and try next address. fd has been closed in __pmConnectTo(). + */ + fd = -1; + continue; + } + + /* FNDELAY and we're in progress - wait on select */ + tv.tv_sec = timeout; + tv.tv_usec = 0; + ptv = (tv.tv_sec || tv.tv_usec) ? &tv : NULL; + __pmFD_ZERO(&wfds); + __pmFD_SET(fd, &wfds); + ret = __pmSelectWrite(fd+1, &wfds, ptv); + + /* Was the connection successful? */ + if (ret <= 0) { + if (oserror() == EINTR) + return -2; + continue; + } + ret = __pmConnectCheckError(fd); + if (ret == 0) + break; + + /* Unsuccessful connection. */ + __pmCloseSocket(fd); + fd = -1; + } /* loop over addresses */ + + __pmHostEntFree(servinfo); + + if(fd == -1) + return -4; + + fd = __pmConnectRestoreFlags(fd, flags); + if(fd < 0) + return -5; + + return fd; +} + +static int stomp_read_ack(void) +{ + struct timeval tv; + fd_set fds, readyfds; + int nready, sts; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = timeout; + tv.tv_usec = 0; + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(fd + 1, &readyfds, NULL, NULL, &tv); + if (nready <= 0) { + if (nready == 0) + __pmNotifyErr(LOG_ERR, "Timed out waiting for server %s:%d - %s", + hostname, port, netstrerror()); + else + __pmNotifyErr(LOG_ERR, "Error waiting for server %s:%d - %s", + hostname, port, netstrerror()); + stomp_disconnect(); + return -1; + } + + do { + sts = recv(fd, buffer, sizeof(buffer), 0); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "Error recving from server %s:%d - %s", + hostname, port, netstrerror()); + stomp_disconnect(); + return -1; + } + /* check for anything else we need to read to clear this ACK */ + memset(&tv, 0, sizeof(tv)); + memcpy(&readyfds, &fds, sizeof(readyfds)); + } while (select(fd + 1, &readyfds, NULL, NULL, &tv) > 0); + + return 0; +} + +static int stomp_write(const char *buffer, int length) +{ + int sts; + + do { + sts = send(fd, buffer, length, 0); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "Write error to JMS server %s:%d - %s", + hostname, port, netstrerror()); + stomp_disconnect(); + return -1; + } + else if (sts == 0) + break; + length -= sts; + } while (length > 0); + + return 0; +} + +static int stomp_authenticate(void) +{ + int len; + + if (fd < 0) + return -1; + len = snprintf(buffer, sizeof(buffer), + "CONNECT\nlogin:%s\npasscode:%s\n\n", username, passcode); + if (stomp_write(buffer, len) < 0) + return -1; + if (stomp_write("\0\n", 2) < 0) + return -1; + return 0; +} + +static int stomp_destination(void) +{ + int len; + + if (fd < 0) + return -1; + len = snprintf(buffer, sizeof(buffer), + "SUB\ndestination:/topic/%s\n\n", topic); + if (stomp_write(buffer, len) < 0) + return -1; + if (stomp_write("\0\n", 2) < 0) + return -1; + return 0; +} + +static int stomp_hello(void) +{ + int len; + + if (fd < 0) + return -1; + len = snprintf(buffer, sizeof(buffer), "SEND\ndestination:/topic/%s\n\n" + "INFO: PMIE: Established initial connection", topic); + if (stomp_write(buffer, len) < 0) + return -1; + if (stomp_write("\0\n", 2) < 0) + return -1; + return 0; +} + +static void stomp_disconnect(void) +{ + if (fd >= 0) + close(fd); + fd = -1; +} + +static char *isspace_terminate(char *string) +{ + int i = 0; + + while (!isspace((int)string[i++])) /* do nothing */ ; + if (i) + string[i-1] = '\0'; + return string; +} + +/* + * Parse our stomp configuration file, simple format: + * host=<hostname> # JMS server machine + * port=<port#> # server port number + * username=<user> | user=<user> + * passcode=<password> | password=<password> + * timeout=<seconds> # optional + * topic=<JMStopic> # optional + */ +static void stomp_parse(void) +{ + char config[MAXPATHLEN+1]; + FILE *f; + int sep = __pmPathSeparator(); + + if (stompfile) + strncat(config, stompfile, sizeof(config)-1); + else + snprintf(config, sizeof(config), + "%s%c" "config" "%c" "pmie" "%c" "stomp", + pmGetConfig("PCP_VAR_DIR"), sep, sep, sep); + if ((f = fopen(config, "r")) == NULL) { + __pmNotifyErr(LOG_ERR, "Cannot open STOMP configuration file %s: %s", + config, osstrerror()); + exit(1); + } + while (fgets(buffer, sizeof(buffer), f)) { + if (strncmp(buffer, "port=", 5) == 0) + port = atoi(isspace_terminate(&buffer[5])); + else if (strncmp(buffer, "host=", 5) == 0) + hostname = strdup(isspace_terminate(&buffer[5])); + else if (strncmp(buffer, "hostname=", 9) == 0) + hostname = strdup(isspace_terminate(&buffer[9])); + else if (strncmp(buffer, "user=", 5) == 0) + username = strdup(isspace_terminate(&buffer[5])); + else if (strncmp(buffer, "username=", 9) == 0) + username = strdup(isspace_terminate(&buffer[9])); + else if (strncmp(buffer, "password=", 9) == 0) + passcode = strdup(isspace_terminate(&buffer[9])); + else if (strncmp(buffer, "passcode=", 9) == 0) + passcode = strdup(isspace_terminate(&buffer[9])); + else if (strncmp(buffer, "timeout=", 8) == 0) /* optional */ + timeout = atoi(isspace_terminate(&buffer[8])); + else if (strncmp(buffer, "topic=", 6) == 0) /* optional */ + topic = strdup(isspace_terminate(&buffer[6])); + } + fclose(f); + + if (!hostname) + __pmNotifyErr(LOG_ERR, "No host in STOMP config file %s", config); + if (port == -1) + __pmNotifyErr(LOG_ERR, "No port in STOMP config file %s", config); + if (!username) + __pmNotifyErr(LOG_ERR, "No username in STOMP config file %s", config); + if (!passcode) + __pmNotifyErr(LOG_ERR, "No passcode in STOMP config file %s", config); + if (port == -1 || !hostname || !username || !passcode) + exit(1); +} + +/* + * Setup the connection to the stomp server, and handle initial protocol + * negotiations (sending user/passcode over to the server in particular). + * Stomp protocol is clear text... (we don't need no stinkin' security!). + * Note: this routine is used for both the initial connection and also for + * any subsequent reconnect attempts. + */ +void stompInit(void) +{ + time_t thistime; + static time_t lasttime; + static int firsttime = 1; + + if (firsttime) { /* initial connection attempt */ + stomp_parse(); + if (!topic) + topic = pmietopic; + atexit(stomp_disconnect); + } else { /* reconnect attempt, if not too soon */ + time(&thistime); + if (thistime < lasttime + 60) + goto disconnect; + } + + if (verbose) + __pmNotifyErr(LOG_INFO, "Connecting to %s, port %d", hostname, port); + if (stomp_connect(hostname, port) < 0) { + __pmNotifyErr(LOG_ERR, "Could not connect to the message server"); + goto disconnect; + } + + if (verbose) + __pmNotifyErr(LOG_INFO, "Connected; sending stomp connect message"); + if (stomp_authenticate() < 0) { + __pmNotifyErr(LOG_ERR, "Could not sent STOMP CONNECT frame to server"); + goto disconnect; + } + + if (verbose) + __pmNotifyErr(LOG_INFO, "Sent; waiting for server ACK"); + if (stomp_read_ack() < 0) { + __pmNotifyErr(LOG_ERR, "Could not read STOMP ACK frame."); + goto disconnect; + } + + if (verbose) + __pmNotifyErr(LOG_INFO, "ACK; sending initial PMIE topic and hello"); + if (stomp_destination() < 0) { + __pmNotifyErr(LOG_ERR, "Could not read TOPIC frame."); + goto disconnect; + } + if (stomp_hello() < 0) { + __pmNotifyErr(LOG_ERR, "Could not send HELLO frame."); + goto disconnect; + } + + if (verbose) + __pmNotifyErr(LOG_INFO, "Sent; waiting for server ACK"); + if (stomp_read_ack() < 0) { + __pmNotifyErr(LOG_ERR, "Could not read STOMP ACK frame"); + goto disconnect; + } + + if (!firsttime) + __pmNotifyErr(LOG_INFO, "Reconnected to STOMP protocol server"); + else if (verbose) + __pmNotifyErr(LOG_INFO, "Initial STOMP protocol setup complete"); + firsttime = 0; + goto finished; + +disconnect: + stomp_disconnect(); + if (firsttime) + exit(1); + /* otherwise, we attempt reconnect on next message firing (>1min) */ +finished: + lasttime = thistime; +} + +/* + * Send a message to the stomp server. + */ +int stompSend(const char *msg) +{ + int len; + + if (fd < 0) stompInit(); /* reconnect */ + if (fd < -1) return -1; + + len = snprintf(buffer, sizeof(buffer), + "SEND\ndestination:/topic/%s\n\n", topic); + if (stomp_write(buffer, len) < 0) + return -1; + if (stomp_write(msg, strlen(msg)) < 0) + return -1; + if (stomp_write("\0\n", 2) < 0) + return -1; + return 0; +} diff --git a/src/pmie/src/stomp.h b/src/pmie/src/stomp.h new file mode 100644 index 0000000..b015efe --- /dev/null +++ b/src/pmie/src/stomp.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006 Aconex. 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 + */ + +/* + * Streaming Text Orientated Messaging Protocol implementation + * http://stomp.codehaus.org/ + */ +extern int stomping; /* true if stomp actions present */ +extern char *stompfile; /* stomp config file */ +extern int stompInit(void); /* connect to stomp server */ +extern int stompSend(const char *); /* send to JMS server, via stomp */ diff --git a/src/pmie/src/symbol.c b/src/pmie/src/symbol.c new file mode 100644 index 0000000..a27bcc4 --- /dev/null +++ b/src/pmie/src/symbol.c @@ -0,0 +1,264 @@ +/*********************************************************************** + * symbol.c - a symbol is an object with a name, a value and a + * reference count + *********************************************************************** + * + * 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. + * + * 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 <stdlib.h> +#include <string.h> +#include "symbol.h" +#include "dstruct.h" +#include "pmapi.h" + + +/*********************************************************************** + * constants + ***********************************************************************/ + +/* bucket memory alignment, address mask and size */ +#undef ALIGN +#define ALIGN 1024 +#undef MASK +#define MASK (ALIGN - 1) +#undef BSIZE +#define BSIZE (ALIGN / sizeof(SymUnion)) + + + +/*********************************************************************** + * types + ***********************************************************************/ + +typedef SymUnion Bucket[BSIZE]; + + + +/*********************************************************************** + * local functions + ***********************************************************************/ + +static void +addBucket(SymUnion *st) +{ + SymUnion *bckt = (SymUnion *) aalloc(ALIGN, sizeof(Bucket)); + SymUnion *scoop = bckt + 1; + + bckt->hdr.prev = st; + bckt->hdr.next = st->hdr.next; + bckt->hdr.free = scoop; + st->hdr.next->hdr.prev = bckt; + st->hdr.next = bckt; + scoop->entry.refs = 0; + scoop->entry.stat.free.ptr = NULL; + scoop->entry.stat.free.count = BSIZE - 1; +} + +static void +remBucket(SymUnion *bckt) +{ + SymUnion *prev = bckt->hdr.prev; + SymUnion *next = bckt->hdr.next; + SymUnion *scan; + int i = 1; + + prev->hdr.next = next; + next->hdr.prev = prev; + while (i < BSIZE) { + scan = bckt + i; + if (scan->entry.refs == 0) + i += scan->entry.stat.free.count; + else { + free(scan->entry.stat.used.name); + i++; + } + } + free(bckt); +} + + + +/*********************************************************************** + * exported functions + ***********************************************************************/ + +/* initialize symbol table */ +void +symSetTable(SymbolTable *st) +{ + /* start with one clean bucket */ + st->hdr.next = st; + st->hdr.prev = st; + addBucket(st); +} + + +/* reset symbol table */ +void +symClearTable(SymbolTable *st) +{ + /* unchain all buckets and free storage */ + while (st->hdr.next != st) + remBucket(st->hdr.next); + /* start with one clean bucket */ + addBucket(st); +} + + +/* Convert string to symbol. A copy of the name string is made + on the heap for use by the symbol. */ +Symbol +symIntern(SymbolTable *st, char *name) +{ + SymUnion *bckt; + SymUnion *scoop = NULL; + char *copy; + int i; + + /* pick up existing symbol */ + bckt = st->hdr.next; + while (bckt != st) { + i = 1; + while (i < BSIZE) { + scoop = bckt + i; + if (scoop->entry.refs) { + if (strcmp(name, scoop->entry.stat.used.name) == 0) { + scoop->entry.refs++; + return scoop; + } + i++; + } + else + i += scoop->entry.stat.free.count; + } + bckt = bckt->hdr.next; + } + + /* pick up free entry */ + bckt = st->hdr.next; + while (bckt != st) { + if ((scoop = bckt->hdr.free)) { + if (scoop->entry.stat.free.count > 1) + scoop += --scoop->entry.stat.free.count; + else + bckt->hdr.free = scoop->entry.stat.free.ptr; + break; + } + bckt = bckt->hdr.next; + } + + /* no free entry - allocate new bucket */ + if (scoop == NULL) { + addBucket(st); + scoop = st->hdr.next + 1; + scoop += --scoop->entry.stat.free.count; + } + + /* initialize symbol */ + scoop->entry.refs = 1; + copy = (char *) alloc(strlen(name) + 1); + strcpy(copy, name); + scoop->entry.stat.used.name = copy; + scoop->entry.stat.used.value = NULL; + return scoop; +} + + +/* lookup symbol by name */ +Symbol +symLookup(SymbolTable *st, char *name) +{ + SymUnion *bckt; + SymUnion *scoop; + int i; + + bckt = st->hdr.next; + while (bckt != st) { + i = 1; + while (i < BSIZE) { + scoop = bckt + i; + if (scoop->entry.refs) { + if (strcmp(name, scoop->entry.stat.used.name) == 0) { + scoop->entry.refs++; + return scoop; + } + i++; + } + else + i += scoop->entry.stat.free.count; + } + bckt = bckt->hdr.next; + } + return NULL; +} + + +/* copy symbol */ +Symbol +symCopy(Symbol sym) +{ + sym->entry.refs++; + return sym; +} + + +/* remove reference to symbol */ +void +symFree(Symbol sym) +{ + SymUnion *bckt; + SymUnion *lead, *lag; + + if ((sym != SYM_NULL) && (--sym->entry.refs <= 0)) { + + /* free up name string BUT NOT value */ + free(sym->entry.stat.used.name); + + /* find correct place in ordered free list */ + bckt = (SymUnion *) ((char *) sym - ((long) sym & MASK)); + lead = bckt->hdr.free; + lag = NULL; + while ((lead != NULL) && (lead < sym)) { + lag = lead; + lead = lead->entry.stat.free.ptr; + } + + if (lag != NULL && (lag + lag->entry.stat.free.count) == sym) { + /* coalesce with preceding free block */ + lag->entry.stat.free.count++; + sym = lag; + } + else { + /* link up as single free entry */ + if (lag) + lag->entry.stat.free.ptr = sym; + else + bckt->hdr.free = sym; + sym->entry.stat.free.count = 1; + sym->entry.stat.free.ptr = lead; + } + + if (sym + sym->entry.stat.free.count == lead) { + /* coalesce with following free block */ + sym->entry.stat.free.count += lead->entry.stat.free.count; + sym->entry.stat.free.ptr = lead->entry.stat.free.ptr; + } + } +} + + diff --git a/src/pmie/src/symbol.h b/src/pmie/src/symbol.h new file mode 100644 index 0000000..07e222a --- /dev/null +++ b/src/pmie/src/symbol.h @@ -0,0 +1,93 @@ +/*********************************************************************** + * symbol.h - a symbol is an object with a name, a value and a + * reference count + *********************************************************************** + * + * 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 SYMBOL_H +#define SYMBOL_H + +/*********************************************************************** + * private + ***********************************************************************/ + +union symunion; /* for forward reference */ + +typedef struct { + union symunion *next; /* forward pointer */ + union symunion *prev; /* backward pointer */ + union symunion *free; /* free list head */ +} SymHdr; + +typedef struct { + union symunion *ptr; /* free list pointer */ + int count; /* number of free entries */ +} SymFree; + +typedef struct { + char *name; /* name string */ + void *value; /* arbitrary value */ +} SymUsed; + +typedef struct { + union { + SymFree free; /* free symbol table entry */ + SymUsed used; /* occupied symbol table entry */ + } stat; + int refs; /* refernce count */ +} SymEntry; + +typedef union symunion { + SymHdr hdr; /* symbol table or bucket header */ + SymEntry entry; /* symbol or free slot */ +} SymUnion; + + +/*********************************************************************** + * public + ***********************************************************************/ + +#define SYM_NULL NULL +typedef SymUnion *Symbol; +typedef SymUnion SymbolTable; + +/* access to name string, value and reference count */ +#define symName(sym) ((sym)->entry.stat.used.name) +#define symValue(sym) ((sym)->entry.stat.used.value) +#define symRefs(sym) ((sym)->entry.refs) + +/* initialize symbol table */ +void symSetTable(SymbolTable *); + +/* reset symbol table */ +void symClearTable(SymbolTable *); + +/* convert string to symbol */ +Symbol symIntern(SymbolTable *, char *); + +/* lookup symbol by name */ +Symbol symLookup(SymbolTable *, char *); + +/* copy symbol */ +Symbol symCopy(Symbol); + +/* remove reference to symbol */ +void symFree(Symbol); + +#endif /* SYMBOL_H */ + diff --git a/src/pmie/src/syntax.c b/src/pmie/src/syntax.c new file mode 100644 index 0000000..46edc27 --- /dev/null +++ b/src/pmie/src/syntax.c @@ -0,0 +1,781 @@ +/*********************************************************************** + * syntax.c - inference rule language parser + *********************************************************************** + * + * Copyright (c) 1995 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * 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 "pmapi.h" +#include "impl.h" + +#include <stdlib.h> +#include <stdio.h> +#ifdef HAVE_REGEX_H +#include <regex.h> +#endif +#include "dstruct.h" +#include "symbol.h" +#include "syntax.h" +#include "lexicon.h" +#include "grammar.h" +#include "pragmatics.h" +#include "eval.h" +#include "show.h" + + + +/*********************************************************************** + * constants + ***********************************************************************/ + +#define NAMEGEN_MAX 12 + + + +/*********************************************************************** + * variables + ***********************************************************************/ + +Symbol parse; /* result of parse */ +int errs; /* error count */ + + +/*********************************************************************** + * miscellaneous local functions + ***********************************************************************/ + +/* generate unique name, only good till next call */ +static char * +nameGen(void) +{ + static int state = 0; + static char bfr[NAMEGEN_MAX]; + + state++; + snprintf(bfr, sizeof(bfr), "expr_%1d", state); + + return bfr; +} + + +/* check domains cardinality agreement */ +static int +checkDoms(Expr *x1, Expr *x2) +{ + if ((x1->nvals != 1) && (x2->nvals != 1)) { + if (x1->hdom != x2->hdom) { + synerr(); + fprintf(stderr, "host domains have different size (%d and %d)\n", + x1->hdom, x2->hdom); + return 0; + } + if (x1->e_idom != x2->e_idom) { + synerr(); + fprintf(stderr, "instance domains have different size (%d and %d)\n", + x1->e_idom, x2->e_idom); + return 0; + } + if (x1->tdom != x2->tdom) { + synerr(); + fprintf(stderr, "time domains have different size (%d and %d)\n", + x1->tdom, x2->tdom); + return 0; + } + } + return 1; +} + + +/* evaluate constant expression */ +static void +evalConst(Expr *x) +{ + if ((x->op < ACT_SHELL) && + (x->arg1->op == NOP) && + ((x->arg2 == NULL) || (x->arg2->op == NOP))) { + (x->eval)(x); + x->op = NOP; + x->eval = NULL; + freeExpr(x->arg1); x->arg1 = NULL; + freeExpr(x->arg2); x->arg2 = NULL; + } +} + + + +/*********************************************************************** + * error reporting + ***********************************************************************/ + +static void +report(char *msg) +{ + LexIn *x = lin; + + fprintf(stderr, "%s: %s - ", pmProgname, msg); + if (x) { + fprintf(stderr, "near line %d of ", x->lno); + if (x->stream) { + if (x->name) fprintf(stderr, "file %s", x->name); + else fprintf(stderr, "standard input"); + } + else { + fprintf(stderr, "macro $%s", x->name); + do x = x->prev; + while (x && x->stream == NULL); + if (x) { + fprintf(stderr, " called from "); + if (x->name) fprintf(stderr, "file %s", x->name); + else fprintf(stderr, "standard input"); + } + } + } + else + fprintf(stderr, "at end of file"); + putc('\n', stderr); +} + +void +synwarn(void) +{ + report("warning"); +} + +void +synerr(void) +{ + report("syntax error"); + errs++; +} + +void +yyerror(char *s) +{ + synerr(); +} + + +/*********************************************************************** + * post processing + * - propagate instance information from fetch expressions towards + * the root of the expression tree, allocating ring buffers etc. + * along the way. + ***********************************************************************/ + +static void +postExpr(Expr *x) +{ + if (x->op == CND_FETCH) { + instFetchExpr(x); + } + else { + if (x->arg1) { + postExpr(x->arg1); + if (x->arg2) + postExpr(x->arg2); + } + } +} + + + +/*********************************************************************** + * parser actions + ***********************************************************************/ + +/* statement */ +Symbol +statement(char *name, Expr *x) +{ + Symbol s; + + /* error guard */ + if (x == NULL) return NULL; + + /* if name not given, make one up */ + if (name == NULL) name = nameGen(); + + /* the parsed object is a rule (expression to evaluate) */ + if (x->op != NOP) { + if (symLookup(&rules, name)) { + synerr(); + fprintf(stderr, "rule \"%s\" multiply defined\n", name); + freeExpr(x); + return NULL; + } + else { + if (errs == 0) { + postExpr(x); + s = symIntern(&rules, name); + } + else return NULL; + } + } + + /* the parsed object is a non-rule */ + else { + if ( (s = symLookup(&vars, name)) ) + freeExpr(symValue(s)); + else + s = symIntern(&vars, name); + } + + symValue(s) = x; + return s; +} + + +/* construct rule Expr */ +Expr * +ruleExpr(Expr *cond, Expr *act) +{ + Expr *x; + + if (cond == NULL) return act; + if (act == NULL) return cond; + + if (cond->nvals != 1) { + synerr(); + fprintf(stderr, "rule condition must have a single boolean value\n"); + } + + perf->numrules++; + + x = newExpr(RULE, cond, act, -1, -1, -1, 1, SEM_BOOLEAN); + newRingBfr(x); + findEval(x); + return x; +} + + +/* action expression */ +Expr * +actExpr(int op, Expr *arg1, Expr *arg2) +{ + Expr *x; + + /* error guard */ + if (arg1 == NULL) return NULL; + + /* construct expression node */ + x = newExpr(op, arg1, arg2, -1, -1, -1, 1, SEM_BOOLEAN); + newRingBfr(x); + findEval(x); + + return x; +} + + +/* action argument expression */ +Expr * +actArgExpr(Expr *arg1, Expr *arg2) +{ + Expr *x; + + /* error guard */ + if (arg1 == NULL) return NULL; + + /* construct expression node */ + x = newExpr(ACT_ARG, arg1, arg2, -1, 1, -1, 1, SEM_CHAR); + newRingBfr(x); + findEval(x); + + return x; +} + +/* action argument */ +Expr * +actArgList(Expr *arg1, char *str) +{ + Expr *x; + + /* construct expression node for an action argument string */ + x = (Expr *) zalloc(sizeof(Expr)); + x->op = NOP; + x->smpls[0].ptr = x->ring = sdup(str); + x->valid = x->nsmpls = x->nvals = 1; + x->tspan = strlen(str); + x->sem = SEM_CHAR; + if (arg1) { + x->arg1 = arg1; + arg1->parent = x; + } + + return x; +} + +/* relational operator expression */ +Expr * +relExpr(int op, Expr *arg1, Expr *arg2) +{ + Expr *x; + Expr *arg; + int i; + int sts; + + /* error guard */ + if (arg1 == NULL || arg2 == NULL) return NULL; + + /* check domains */ + sts = checkDoms(arg1, arg2); + + /* decide primary argument for inheritance of Expr attributes */ + arg = primary(arg1, arg2); + + /* construct expression node */ + x = newExpr(op, arg1, arg2, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), SEM_BOOLEAN); +#if PCP_DEBUG + if (sts == 0 && (pmDebug & DBG_TRACE_APPL1)) { + fprintf(stderr, "relExpr: checkDoms(" PRINTF_P_PFX "%p, " PRINTF_P_PFX "%p) failed ...\n", arg1, arg2); + __dumpTree(1, x); + } +#endif + newRingBfr(x); + if (x->tspan > 0) { + for (i = 0; i < x->nsmpls; i++) + *((char *)x->ring + i) = B_UNKNOWN; + } + findEval(x); + + /* evaluate constant expression now */ + evalConst(x); + + return x; +} + + +/* binary operator expression */ +Expr * +binaryExpr(int op, Expr *arg1, Expr *arg2) +{ + Expr *x; + Expr *arg = arg1; + int sts = 0; + + /* error guard */ + if (arg1 == NULL) return NULL; + + if (arg1 != NULL && arg2 != NULL) { + if (op != CND_MATCH && op != CND_NOMATCH) { + /* check domains */ + sts = checkDoms(arg1, arg2); + + /* decide primary argument for inheritance of Expr attributes */ + arg = primary(arg1, arg2); + } + else { + regex_t *pat; + + pat = alloc(sizeof(*pat)); + if (regcomp(pat, (char *)arg2->ring, REG_EXTENDED|REG_NOSUB) != 0) { + /* bad pattern */ + fprintf(stderr, "illegal regular expression \"%s\"\n", (char *)arg2->ring); + free(pat); + return NULL; + } +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) { + fprintf(stderr, "binaryExpr: regex=\"%s\" handle=" PRINTF_P_PFX "%p\n", (char *)arg2->ring, pat); + } +#endif + /* + * change operand from the string form of the pattern to the + * compiled regex + */ + free(arg2->ring); + arg2->tspan = 1; + arg2->ring = pat; + arg2->sem = SEM_REGEX; + sts = 1; + } + } + + /* construct expression node */ + x = newExpr(op, arg1, arg2, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), arg->sem); +#if PCP_DEBUG + if (sts == 0 && (pmDebug & DBG_TRACE_APPL1)) { + fprintf(stderr, "binaryExpr: checkDoms(" PRINTF_P_PFX "%p, " PRINTF_P_PFX "%p) failed ...\n", arg1, arg2); + __dumpTree(1, x); + } +#endif + newRingBfr(x); + findEval(x); + + /* evaluate constant expression now */ + evalConst(x); + + return x; +} + + +/* unary operator expression */ +Expr * +unaryExpr(int op, Expr *arg) +{ + Expr *x; + + /* error guard */ + if (arg == NULL) return NULL; + + /* construct expression node */ + x = newExpr(op, arg, NULL, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), arg->sem); + newRingBfr(x); + findEval(x); + + /* evaluate constant expression now */ + evalConst(x); + + return x; +} + + +/* aggregation/quantification operator expression */ +Expr * +domainExpr(int op, int dom, Expr *arg) +{ + Expr *x; + int hdom; + int idom; + int tdom; + + /* error guard */ + if (arg == NULL) return NULL; + + hdom = arg->hdom; + idom = arg->e_idom; + tdom = arg->tdom; + + switch (dom) { + case HOST_DOM: + if (hdom == -1) { + synerr(); + fprintf(stderr, "no host domain\n"); + freeExpr(arg); + return NULL; + } + hdom = -1; + idom = -1; + dom = 0; + break; + case INST_DOM: +#if 0 + /* + * I believe this test is no longer correct ... the instance + * domain may be unobtainable at this point, or may change + * later so checking at the syntactic level is not helpful + */ + if (idom == -1) { + synerr(); + fprintf(stderr, "no instance domain\n"); + freeExpr(arg); + return NULL; + } +#endif + idom = -1; + dom = 1; + break; + case TIME_DOM: + if (tdom == -1) { + synerr(); + fprintf(stderr, "no time domain\n"); + freeExpr(arg); + return NULL; + } + tdom = -1; + dom = 2; + } + + if (op == CND_COUNT_HOST) { + x = newExpr(op + dom, arg, NULL, hdom, idom, tdom, abs(tdom), PM_SEM_INSTANT); + newRingBfr(x); + x->units = countUnits; + } + else { + x = newExpr(op + dom, arg, NULL, hdom, idom, tdom, abs(tdom), arg->sem); + newRingBfr(x); + } + + findEval(x); + return x; +} + + +/* percentage quantifier */ +Expr * +percentExpr(double pcnt, int dom, Expr *arg) +{ + Expr *x; + + /* error guard */ + if (arg == NULL) return NULL; + + x = domainExpr(CND_PCNT_HOST, dom, arg); + x->arg2 = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_NUMCONST); + newRingBfr(x->arg2); + *(double *)x->arg2->ring = pcnt / 100.0; + x->arg2->valid = 1; + return x; +} + + +/* merge expression */ +static Expr * +mergeExpr(int op, Expr *arg) +{ + Expr *x; + + /* force argument to buffer at least two samples */ + if (arg->nsmpls < 2) + changeSmpls(&arg, 2); + + /* construct expression node */ + x = newExpr(op, arg, NULL, arg->hdom, arg->e_idom, arg->tdom, abs(arg->tdom), arg->sem); + newRingBfr(x); + findEval(x); + return x; +} + + +/* numeric merge expression */ +Expr * +numMergeExpr(int op, Expr *arg) +{ + /* error guard */ + if (arg == NULL) return NULL; + + /* check argument semantics */ + if ((arg->sem == SEM_BOOLEAN) || (arg->sem == SEM_CHAR)) { + synerr(); + fprintf(stderr, "operator \"%s\" requires numeric valued argument\n", opStrings(op)); + return NULL; + } + + return mergeExpr(op, arg); +} + + +/* boolean merge expression */ +Expr * +boolMergeExpr(int op, Expr *arg) +{ + /* error guard */ + if (arg == NULL) return NULL; + + /* check argument semantics */ + if (arg->sem != SEM_BOOLEAN) { + synerr(); + fprintf(stderr, "operator \"%s\" requires boolean valued argument\n", opStrings(op)); + return NULL; + } + + return mergeExpr(op, arg); +} + + +/* fetch expression */ +Expr * +fetchExpr(char *mname, + StringArray hnames, + StringArray inames, + Interval times) +{ + Expr *x; + Metric *marr, *m; + int fsz, dsz; + int sum; + int i; + + /* calculate samplecounts for fetch and delay */ + if (times.t1 == 0) { + fsz = times.t2 - times.t1 + 1; + dsz = 0; + } + else { + fsz = times.t1 + 1; + dsz = times.t2 - times.t1 + 1; + } + + /* default host */ + if (hnames.n == 0) { + hnames.n = 1; + } + + /* construct Metrics array */ + marr = m = (Metric *) zalloc(hnames.n * sizeof(Metric)); + sum = 0; + for (i = 0; i < hnames.n; i++) { + m->mname = symIntern(&metrics, mname); + if (hnames.ss) m->hname = symIntern(&hosts, hnames.ss[i]); + else m->hname = symIntern(&hosts, dfltHostName); + m->desc.sem = SEM_UNKNOWN; + m->m_idom = -1; + if (inames.n > 0) { + m->specinst = inames.n; + m->iids = alloc(inames.n * sizeof(int)); + m->inames = inames.ss; + } + else { + m->specinst = 0; + m->iids = NULL; + m->inames = NULL; + } + if (errs == 0) { + int sts = initMetric(m); + if (sts < 0) errs++; + if (m->m_idom > 0) + sum += m->m_idom; + } + m++; + } + if (sum == 0) + sum = -1; + + /* error exit */ + if (errs) { + m = marr; + for (i = 0; i < hnames.n; i++) { + if (m->iids) free(m->iids); + m++; + } + free(marr); + return NULL; + } + + /* construct fetch node */ + x = newExpr(CND_FETCH, NULL, NULL, hnames.n, sum, fsz, fsz, SEM_UNKNOWN); + newRingBfr(x); + x->metrics = marr; + findEval(x); + instFetchExpr(x); + + /* patch in fetch node reference in each Metric */ + m = marr; + for (i = 0; i < hnames.n; i++) { + m->expr = x; + m++; + } + + /* construct delay node */ + if (dsz) { + x = newExpr(CND_DELAY, x, NULL, x->hdom, x->e_idom, dsz, dsz, SEM_UNKNOWN); + newRingBfr(x); + findEval(x); + } + return x; +} + + +/* numeric constant */ +Expr * +numConst(double v, pmUnits u) +{ + Expr *x; + + x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_NUMCONST); + newRingBfr(x); + x->units = canon(u); + x->valid = 1; + *(double *) x->ring = scale(u) * v; + return x; +} + + +/* string constant */ +Expr * +strConst(char *s) +{ + Expr *x; + int n = (int) strlen(s) + 1; + + x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_CHAR); + x->valid = 1; + x->tspan = n; + newRingBfr(x); + strcpy((char *)x->ring, s); + return x; +} + + +/* boolean constant */ +Expr * +boolConst(Boolean v) +{ + Expr *x; + + x = newExpr(NOP, NULL, NULL, -1, -1, -1, 1, SEM_BOOLEAN); + newRingBfr(x); + x->valid = 1; + *(Boolean *) x->ring = v; + return x; +} + + +/* numeric valued variable */ +Expr * +numVar(Expr *x) +{ + if (x->sem == SEM_BOOLEAN || x->sem == SEM_CHAR) { + synerr(); + fprintf(stderr, "numeric valued variable expected\n"); + return NULL; + } + return x; +} + + +/* truth valued variable */ +Expr * +boolVar(Expr *x) +{ + if (x->sem == SEM_CHAR) { + synerr(); + fprintf(stderr, "truth valued variable expected\n"); + return NULL; + } + return x; +} + + +/*********************************************************************** + * parser + ***********************************************************************/ + +/* Initialization to be called at the start of new input file. */ +int +synInit(char *fname) +{ + return lexInit(fname); +} + + +/* parse single statement */ +Symbol +syntax(void) +{ + while (lexMore()) { + errs = 0; + parse = NULL; + yyparse(); + if (parse) + return parse; + } + lexFinal(); + return NULL; +} + + + diff --git a/src/pmie/src/syntax.h b/src/pmie/src/syntax.h new file mode 100644 index 0000000..f01228f --- /dev/null +++ b/src/pmie/src/syntax.h @@ -0,0 +1,97 @@ +/*********************************************************************** + * syntax.h - inference rule language parser + *********************************************************************** + * + * 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 SYNTAX_H +#define SYNTAX_H + + +/*********************************************************************** + * ONLY FOR USE BY: lexicon.c grammar.y + ***********************************************************************/ + +typedef struct { + int n; /* number of elements */ + char **ss; /* dynamically allocated array */ +} StringArray; + +typedef struct { + int t1; /* start of interval */ + int t2; /* end of interval */ +} Interval; + +/* parser stack entry */ +typedef union { + int i; + char *s; + double d; + StringArray sa; + Interval t; + pmUnits u; + Metric *m; + Expr *x; + Symbol *z; +} YYSTYPE; +#define YYSTYPE_IS_DECLARED 1 + +extern YYSTYPE yylval; + +/* error reporting */ +extern int errs; /* error count */ +void yyerror(char *); +void synerr(void); +void synwarn(void); + +/* parser actions */ +Symbol statement(char *, Expr *); +Expr *ruleExpr(Expr *, Expr *); +Expr *relExpr(int, Expr *, Expr *); +Expr *binaryExpr(int, Expr *, Expr *); +Expr *unaryExpr(int, Expr *); +Expr *domainExpr(int, int, Expr *); +Expr *percentExpr(double, int, Expr *); +Expr *numMergeExpr(int, Expr *); +Expr *boolMergeExpr(int, Expr *); +Expr *fetchExpr(char *, StringArray, StringArray, Interval); +Expr *numConst(double, pmUnits); +Expr *strConst(char *); +Expr *boolConst(Boolean); +Expr *numVar(Expr *); +Expr *boolVar(Expr *); +Expr *actExpr(int, Expr *, Expr *); +Expr *actArgExpr(Expr *, Expr *); +Expr *actArgList(Expr *, char *); + +/* parse tree */ +extern Symbol parse; + + + +/*********************************************************************** + * public + ***********************************************************************/ + +/* Initialization to be called at the start of new input file. */ +int synInit(char *); + +/* parse single statement */ +Symbol syntax(void); + +#endif /* SYNTAX_H */ + diff --git a/src/pmie/src/systemlog.c b/src/pmie/src/systemlog.c new file mode 100644 index 0000000..a493841 --- /dev/null +++ b/src/pmie/src/systemlog.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1999-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 <ctype.h> +#define SYSLOG_NAMES +#include "dstruct.h" +#include "eval.h" +#include "syntax.h" +#include "systemlog.h" +#include "pmapi.h" + +#if defined(IS_SOLARIS) || defined(IS_AIX) || defined(IS_MINGW) +#include "logger.h" +#endif + +/* + * based on source for logger(1) + */ +static int +decode(char *name, CODE *codetab) +{ + CODE *c; + + if (isdigit((int)*name)) + return (atoi(name)); + + for (c = codetab; c->c_name; c++) + if (!strcasecmp(name, c->c_name)) + return (c->c_val); + + return (-1); +} + +/* + * Decode a symbolic name to a numeric value + * ... based on source for logger(1) + */ +static int +pencode(char *s) +{ + char *save; + int fac, lev; + + save = s; + while (*s && *s != '.') ++s; + if (*s) { + *s = '\0'; + fac = decode(save, facilitynames); + if (fac < 0) { + synwarn(); + fprintf(stderr, "Ignoring unknown facility (%s) for -p in syslog action\n", save); + fac = 0; + } + s++; + } + else { + fac = 0; + s = save; + } + lev = decode(s, prioritynames); + if (lev < 0) { + synwarn(); + fprintf(stderr, "Ignoring unknown priority (%s) for -p in syslog action\n", s); + lev = LOG_NOTICE; + } + return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK)); +} + +/* + * handle splitting of -t tag and -p prio across one or two + * arguments to the syslog action in the rule + */ +static void +nextch(char **p, Expr **x) +{ + static char end = '\0'; + (*p)++; + while (**p == '\0') { + if ((*x)->arg1 == NULL) { + *p = &end; + return; + } + *x = (*x)->arg1; + *p = (*x)->ring; + } +} + +/* + * post-process the expression tree for a syslog action to gather + * any -t tag or -p pri options (as for logger(1)) and build the + * encoded equivalent as a new expression accessed via arg2 from + * the head of the arguments list + */ +void +do_syslog_args(Expr *act) +{ + int pri = -1; + char *tag = NULL; + char *p; + char *q; + Expr *others; + Expr *tmp; + Expr *new; + + /* + * scan for -p pri and -t tag + */ + for (others = act->arg1; others != NULL; ) { + if (others->ring == NULL) break; + p = others->ring; + if (*p != '-') break; + nextch(&p, &others); + if (*p == 'p' && pri == -1) { + nextch(&p, &others); + while (*p && isspace((int)*p)) nextch(&p, &others); + if (*p == '\0') { + synwarn(); + fprintf(stderr, "Missing [facility.]priority after -p in syslog action\n"); + } + else { + q = p+1; + while (*q && !isspace((int)*q)) q++; + if (*q) { + synwarn(); + fprintf(stderr, "Ignoring extra text (%s) after -p pri in syslog action\n", q); + *q = '\0'; + } + pri = pencode(p); + } + } + else if (*p == 't' && tag == NULL) { + nextch(&p, &others); + while (*p && isspace((int)*p)) nextch(&p, &others); + if (*p == '\0') { + synwarn(); + fprintf(stderr, "Missing tag after -t in syslog action\n"); + } + else { + q = p+1; + while (*q && !isspace((int)*q)) q++; + if (*q) { + synwarn(); + fprintf(stderr, "Ignoring extra text (%s) after -t tag in syslog action\n", q); + *q = '\0'; + } + tag = p; + } + } + else + break; + others = others->arg1; + } + + /* defaults if -t and/or -p not seen */ + if (pri < 0) pri = LOG_NOTICE; + if (tag == NULL) tag = "pcp-pmie"; + + /* + * construct new arg2 argument node, with + * ring -> pri (int) and tag (char *) concatenated + */ + new = (Expr *) zalloc(sizeof(Expr)); + new->op = NOP; + new->ring = (char *)alloc(sizeof(int)+strlen(tag)+1); + *((int *)new->ring) = pri; + strcpy(&((char *)new->ring)[sizeof(int)], tag); + act->arg2 = new; + new->parent = act; + + /* free old argument nodes used for -p and/or -t specifications */ + for (tmp = act->arg1; tmp != others; ) { + if (tmp->ring) { + free(tmp->ring); + } + new = tmp->arg1; + free(tmp); + tmp = new; + } + + /* re-link remaining argument nodes */ + if (others != act->arg1) { + act->arg1 = others; + if (others != NULL) + others->parent = act; + } + +} diff --git a/src/pmie/src/systemlog.h b/src/pmie/src/systemlog.h new file mode 100644 index 0000000..fba5151 --- /dev/null +++ b/src/pmie/src/systemlog.h @@ -0,0 +1 @@ +void do_syslog_args(Expr *); diff --git a/src/pmie/src/unary.sk b/src/pmie/src/unary.sk new file mode 100644 index 0000000..cbf3839 --- /dev/null +++ b/src/pmie/src/unary.sk @@ -0,0 +1,91 @@ +/* + * 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. + * + * 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 + */ + +/*********************************************************************** + * skeleton: unary.sk - unary operator + ***********************************************************************/ + +/* + * operator: @FUN + */ + +#define @OP + +void +@FUN_n(Expr *x) +{ + Expr *arg1 = x->arg1; + Sample *is = &arg1->smpls[0]; + Sample *os = &x->smpls[0]; + @ITYPE *ip; + @OTYPE *op; + int n; + int i; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid && x->tspan > 0) { + ip = (@ITYPE *) is->ptr; + op = (@OTYPE *) os->ptr; + n = x->tspan; + for (i = 0; i < n; i++) { + *op = OP(*ip); + op++; + ip++; + } + os->stamp = is->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_n(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +void +@FUN_1(Expr *x) +{ + Expr *arg1 = x->arg1; + Sample *is = &arg1->smpls[0]; + Sample *os = &x->smpls[0]; + + EVALARG(arg1) + ROTATE(x) + + if (arg1->valid) { + *(@OTYPE *)os->ptr = OP(*(@ITYPE *)is->ptr); + os->stamp = is->stamp; + x->valid++; + } + else x->valid = 0; + +#if PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL2) { + fprintf(stderr, "@FUN_1(" PRINTF_P_PFX "%p) ...\n", x); + dumpExpr(x); + } +#endif +} + +#undef OP + diff --git a/src/pmie/stomp b/src/pmie/stomp new file mode 100644 index 0000000..7c93fec --- /dev/null +++ b/src/pmie/stomp @@ -0,0 +1,11 @@ +# +# Sample STOMP configuration file, parameters affecting connection +# between pmie and a JMS server for the "stomp" rule action. +# + +host=foo.bar.com # this is the JMS server (required) +port=61616 # and its listening here (required) +timeout=2 # seconds to wait for server (optional) +topic=PMIE # JMS topic for pmie messages (optional) +username=joe # required +password=j03ST0MP # required |