diff options
Diffstat (limited to 'src/pmdas/trace')
-rw-r--r-- | src/pmdas/trace/GNUmakefile | 109 | ||||
-rw-r--r-- | src/pmdas/trace/GNUmakefile.stub | 69 | ||||
-rw-r--r-- | src/pmdas/trace/Install | 275 | ||||
-rw-r--r-- | src/pmdas/trace/Makefile.proto | 69 | ||||
-rw-r--r-- | src/pmdas/trace/README | 62 | ||||
-rw-r--r-- | src/pmdas/trace/README.demos | 71 | ||||
-rw-r--r-- | src/pmdas/trace/Remove | 38 | ||||
-rw-r--r-- | src/pmdas/trace/app1.c | 97 | ||||
-rw-r--r-- | src/pmdas/trace/app2.c | 163 | ||||
-rw-r--r-- | src/pmdas/trace/app3.c | 166 | ||||
-rw-r--r-- | src/pmdas/trace/fapp1.f | 102 | ||||
-rw-r--r-- | src/pmdas/trace/help | 156 | ||||
-rw-r--r-- | src/pmdas/trace/japp1.java | 46 | ||||
-rw-r--r-- | src/pmdas/trace/pmns | 62 | ||||
-rw-r--r-- | src/pmdas/trace/root | 10 | ||||
-rw-r--r-- | src/pmdas/trace/src/GNUmakefile | 57 | ||||
-rw-r--r-- | src/pmdas/trace/src/client.c | 155 | ||||
-rw-r--r-- | src/pmdas/trace/src/client.h | 38 | ||||
-rw-r--r-- | src/pmdas/trace/src/comms.c | 289 | ||||
-rw-r--r-- | src/pmdas/trace/src/comms.h | 26 | ||||
-rw-r--r-- | src/pmdas/trace/src/data.c | 122 | ||||
-rw-r--r-- | src/pmdas/trace/src/data.h | 68 | ||||
-rw-r--r-- | src/pmdas/trace/src/pmda.c | 228 | ||||
-rw-r--r-- | src/pmdas/trace/src/trace.c | 1151 | ||||
-rw-r--r-- | src/pmdas/trace/stub.c | 136 |
25 files changed, 3765 insertions, 0 deletions
diff --git a/src/pmdas/trace/GNUmakefile b/src/pmdas/trace/GNUmakefile new file mode 100644 index 0000000..64d2512 --- /dev/null +++ b/src/pmdas/trace/GNUmakefile @@ -0,0 +1,109 @@ +# +# Copyright (c) 2000-2001,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 + +IAM = trace +DOMAIN = TRACE + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +DEMODIR = $(PCP_DEMOS_DIR)/$(IAM) + +SCRIPTS = Install Remove +OTHERS = pmns root +DFILES = help README +DEMOS = app1.c app2.c app3.c fapp1.f japp1.java +DEMOFILES = README.demos Makefile.proto GNUmakefile.stub stub.c +APPS = app1$(EXECSUFFIX) app2$(EXECSUFFIX) app3$(EXECSUFFIX) + +LCFLAGS = -I. +LLDFLAGS= -L$(TOPDIR)/src/libpcp/src -L$(TOPDIR)/src/libpcp_trace/src +LLDLIBS = $(PCP_TRACELIB) + +LDIRT = *.log *.dir *.pag *.o $(APPS) tmp.c \ + pmtrace.c Makefile.demos + +LSRCFILES= $(SCRIPTS) $(OTHERS) $(DEMOS) $(DEMOFILES) $(DFILES) + +SUBDIRS = src + +default: $(SUBDIRS) Makefile.demos build-me + $(SUBDIRS_MAKERULE) + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: demos + +install: $(SUBDIRS) Makefile.demos + $(SUBDIRS_MAKERULE) + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(SCRIPTS) $(PMDADIR) + $(INSTALL) -m 644 $(OTHERS) $(DFILES) $(PMDADIR) + $(INSTALL) -m 755 -d $(DEMODIR) + $(INSTALL) -m 644 Makefile.demos $(DEMODIR)/Makefile + $(INSTALL) -m 644 README.demos $(DEMODIR)/README + $(INSTALL) -m 644 GNUmakefile.stub $(DEMODIR)/Makefile.stub + $(INSTALL) -m 644 stub.c pmtrace.c $(DEMOS) $(DEMODIR) + +else +build-me: +install: $(SUBDIRS) +endif + +demos: $(APPS) pmtrace.c + +pmtrace.c: $(TOPDIR)/src/pmtrace/pmtrace.c + rm -f $@ && cp $< $@ + +app1.o: app1.c $(TOPDIR)/src/include/pcp/pmapi.h + @rm -f tmp.c + sed -e 's;<pcp/\(.*\)>;"\1";' app1.c >tmp.c + $(CCF) -c -o $@ tmp.c + @rm -f tmp.c + +app2.o: app2.c $(TOPDIR)/src/include/pcp/pmapi.h + @rm -f tmp.c + sed -e 's;<pcp/\(.*\)>;"\1";' app2.c >tmp.c + $(CCF) -c -o $@ tmp.c + @rm -f tmp.c + +app3.o: app3.c $(TOPDIR)/src/include/pcp/pmapi.h + @rm -f tmp.c + sed -e 's;<pcp/\(.*\)>;"\1";' app3.c >tmp.c + $(CCF) -c -o $@ tmp.c + @rm -f tmp.c + +app1$(EXECSUFFIX): app1.o + $(CCF) -o $@ $(LDFLAGS) app1.o $(LDLIBS) + +app2$(EXECSUFFIX): app2.o + $(CCF) -o $@ $(LDFLAGS) app2.o $(LDLIBS) + +app3$(EXECSUFFIX): app3.o + $(CCF) -o $@ $(LDFLAGS) app3.o $(LDLIBS) $(LIB_FOR_PTHREADS) + +default_pcp: default + +install_pcp: install + +.NOTPARALLEL: +Makefile.demos: Makefile.proto + rm -f $@ + sed \ + -e 's/PTHREAD_LIB/$(LIB_FOR_PTHREADS)/' \ + -e 's/DLOPEN_LIB/$(LIB_FOR_DLOPEN)/' \ + -e 's/MATH_LIB/$(LIB_FOR_MATH)/' \ + <$^ >$@ diff --git a/src/pmdas/trace/GNUmakefile.stub b/src/pmdas/trace/GNUmakefile.stub new file mode 100644 index 0000000..8ac95ae --- /dev/null +++ b/src/pmdas/trace/GNUmakefile.stub @@ -0,0 +1,69 @@ +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +include $(PCP_ETC_DIR)/pcp.conf + +# need to deal with these ... +# ELF style +# /usr/lib/libpcp_trace.a /usr/lib/libpcp_trace.so /usr/lib/libpcp_trace.so.2 +# Mac OS X style +# /usr/lib/libpcp_trace.2.dylib +# +DSO_SUFFIX = $(shell ls $(PCP_LIB_DIR)/libpcp_trace.* | sed -e '/\.a$$/d' -e 's/.*libpcp_trace//' -e 's/\.[0-9][0-9]*//' -e 's/^\.//' | sed -e 1q) +ifeq "$(DSO_SUFFIX)" "" +$(error cannot set DSO_SUFFIX on this platform) +endif + +# Note: DSO_VERSION includes (starts with or ends with) $(DSO_SUFFIX) +# +DSO_VERSION = $(shell ls $(PCP_LIB_DIR)/libpcp_trace.* | sed -n -e '/\.a$$/d' -e 's/.*libpcp_trace\.//' -e '/\.$(DSO_SUFFIX)/p' -e '/$(DSO_SUFFIX)\./p' | sed -e 's/^\.//' -e 1q) +ifeq "$(DSO_VERSION)" "" +$(error cannot set DSO_VERSION on this platform) +endif + +SHELL = /bin/sh +CC = cc +TARGETS = lib/libpcp_trace.$(DSO_VERSION) +ifeq "$(shell [ -f $(PCP_LIB_DIR)/libpcp_trace.$(DSO_SUFFIX) ] && echo 1)" "1" +TARGETS += lib/libpcp_trace.$(DSO_SUFFIX) +endif +CFILES = stub.c +CFLAGS += -DPMTRACE_DEBUG +CFLAGS += -fPIC -fno-strict-aliasing +LDIRT = lib lib32 lib64 + +default: $(TARGETS) + +lib/libpcp_trace.$(DSO_VERSION): stub.c + -[ ! -d lib ] && mkdir lib + rm -f $@ + cd lib; $(CC) $(CFLAGS) -shared ../stub.c -o libpcp_trace.$(DSO_VERSION) + +lib/libpcp_trace.$(DSO_SUFFIX): lib/libpcp_trace.$(DSO_VERSION) + rm -f $@ + cd lib; ln -s libpcp_trace.$(DSO_VERSION) libpcp_trace.$(DSO_SUFFIX) + +clean: + rm -rf $(LDIRT) + +clobber: + rm -rf $(LDIRT) $(TARGETS) + +debug: + @echo "DSO_SUFFIX=$(DSO_SUFFIX)" + @echo "DSO_VERSION=$(DSO_VERSION)" + @echo "TARGETS=$(TARGETS)" diff --git a/src/pmdas/trace/Install b/src/pmdas/trace/Install new file mode 100644 index 0000000..f092d4f --- /dev/null +++ b/src/pmdas/trace/Install @@ -0,0 +1,275 @@ +#! /bin/sh +# +# Copyright (c) 1997,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. +# +# Install the Trace PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=trace +pmda_interface=2 +forced_restart=false + +# Override interactive dialog from pmdaSetup in pmdaproc.sh +# +__choose_mode() +{ + echo "Installing the \"$iam\" Performance Metrics Domain Agent (PMDA) ..." + echo +} + +numeric=0 + +getnumeric() +{ + if [ "X$2" = "X" ] + then + numeric=0 + elif [ "X`expr 0 + $2 2>/dev/null`" != "X$2" ] + then + echo "-- Sorry, $1 must be numeric (not $2) --" + return 1 + else + numeric=$2 + fi + return 0 +} + +getunits() +{ + metric_name=$1 + option_name=$2 + + # Default dimension and scale values + # + dimspace=0 + dimtime=0 + dimcount=0 + scalespace=0 + scaletime=0 + scalecount=0 + + cat - <<EOF + +The dimension and scale for the ${metric_name} metrics may be expressed +in terms of Space, Time and Count (i.e. events or messages). The default +dimension and scale is "None". Do you wish to accept the default dimension +EOF + $PCP_ECHO_PROG $PCP_ECHO_N "and scale [y]? ""$PCP_ECHO_C" + + read ans + unitschange=false + case $ans in + N|n|NO|No|no) unitschange=true;; + *) unitschange=false;; + esac + + if [ $unitschange = true ] + then + echo +echo "The dimension is expressed in terms of powers of Space, Time and Count." + echo "For example, bytes per second would be 1 -1 0, and milliseconds per message " + echo "would be 0 1 -1." + while true + do + $PCP_ECHO_PROG $PCP_ECHO_N "Enter the dimension for Space, Time and Count [0 0 0]: ""$PCP_ECHO_C" + dimspace=0; dimtime=0; dimcount=0 + read s t c + + getnumeric 'Scale dimension' $s + [ $? -eq 1 ] && continue + dimspace=$numeric + + getnumeric 'Time dimension' $t + [ $? -eq 1 ] && continue + dimtime=$numeric + + getnumeric 'Count dimension' $c + [ $? -eq 1 ] && continue + dimcount=$numeric + + break + done + + if [ "$dimspace" != 0 ] + then + while true + do + echo "Scale for the Space dimension is expressed as:" + echo " 0 (bytes)" + echo " 1 (kilobytes)" + echo " 2 (megabytes)" + echo " 3 (gigabytes)" + echo " 4 (terabytes)" + $PCP_ECHO_PROG $PCP_ECHO_N 'Enter the scale for the Space dimension [0]: '"$PCP_ECHO_C" + read scale + scalespace=0 + + getnumeric 'Space scale' $scale + [ $? -eq 1 ] && continue + if [ $numeric -lt 0 -o $numeric -gt 4 ] + then + echo "-- Sorry, Space scale must be between 0 and 4 --" + continue + fi + scalespace=$numeric + break + done + fi + + if [ "$dimtime" != 0 ] + then + while true + do + echo "Scale for the Time dimension is expressed as:" + echo " 0 (nanoseconds)" + echo " 1 (microseconds)" + echo " 2 (milliseconds)" + echo " 3 (seconds)" + echo " 4 (minutes)" + echo " 5 (hours)" + $PCP_ECHO_PROG $PCP_ECHO_N "Enter the scale for the Time dimension: ""$PCP_ECHO_C" + read scale + scaletime=0 + + getnumeric 'Time scale' $scale + [ $? -eq 1 ] && continue + if [ $numeric -lt 0 -o $numeric -gt 5 ] + then + echo "-- Sorry, Time scale must be between 0 and 5 --" + continue + fi + scaletime=$numeric + break + done + fi + + if [ "$dimcount" != 0 ] + then + while true + do + echo "Scale for the Count dimension is expressed:" + $PCP_ECHO_PROG $PCP_ECHO_N " as a power of 10 (e.g. 6 for 10^6, or -3 for 10^-3) [0]: ""$PCP_ECHO_C" + read scale + scalecount=0 + + getnumeric 'Count scale' $scale + [ $? -eq 1 ] && continue + scalecount=$numeric + break + done + fi + fi + + # show what units we're going with .. + echo + echo "Using the following units for ${metric_name}:" + echo " Dimensions: space=$dimspace time=$dimtime count=$dimcount" + echo " Scale: space=$scalespace time=$scaletime count=$scalecount" + echo + + if [ $unitschange = true ] + then + uarg="$dimspace,$dimtime,$dimcount,$scalespace,$scaletime,$scalecount" + args="$args ${option_name} $uarg" + fi +} + + +pmdaSetup + +$PCP_ECHO_PROG $PCP_ECHO_N "Use the default installation [y]? ""$PCP_ECHO_C" +read nogo +goforit=false +case $nogo in + n|no|N|NO|No) goforit=true;; + *) goforit=false;; +esac +echo +if [ $goforit = false ] +then + : +elif $do_pmda +then + args="" + value="" + + $PCP_ECHO_PROG $PCP_ECHO_N "Trace period (in seconds) [60]? ""$PCP_ECHO_C" + read value + getnumeric 'Trace period' $value >/dev/null + [ $? -ne 1 -a $numeric -ne 0 ] && args="-T $numeric" + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Number of buckets [5]? ""$PCP_ECHO_C" + read value + getnumeric 'Number of buckets' $value >/dev/null + [ $? -ne 1 -a $numeric -ne 0 ] && args="$args -N $numeric" + + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Port number for client connections [4323]? ""$PCP_ECHO_C" + read value + getnumeric 'Port number' $value >/dev/null + [ $? -ne 1 -a $numeric -ne 0 ] && args="$args -I $numeric" + + getunits trace.observe.value -U + getunits trace.counter.value -V + + while true + do + access="" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Client host access - (A)llow/(D)isallow [Enter to complete install]? ""$PCP_ECHO_C" + read access + access=`echo $access | tr -d ' '` + [ "X$access" = "X" ] && break + case $access in + A|a|Allow|allow) + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Host specification (IP mask/Enter to cancel): ""$PCP_ECHO_C" + read access + access=`echo $access | tr -d ' '` + if [ "X$access" != "X" ] + then + maxconns="" + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Maximum number of connections from $access (Enter for no limit): ""$PCP_ECHO_C" + read maxconns + maxconns=`echo $maxconns | tr -d ' '` + [ "X$maxconns" = "X" ] && maxconns=0 + args="$args"" -A ""allow:$access:$maxconns" + fi;; + D|d|Disallow|disallow) + echo + $PCP_ECHO_PROG $PCP_ECHO_N "Host specification (IP mask/Enter to cancel): ""$PCP_ECHO_C" + read access + access=`echo $access | tr -d ' '` + [ "X$access" != "X" ] && args="$args"" -A ""disallow:$access";; + *) echo 'Try again, "'$access'" not supported.';; + esac + done + echo +fi + +# for debugging the PMDA, uncomment this line ... +# +#args="-D appl0,appl1,pdu $args" + +# Do it ... +# +pmdaInstall +echo Note: some warnings are expected until trace API calls are made - refer to +echo " the man pages for pmtrace(1) and pmdatrace(3) for further details." + +exit 0 diff --git a/src/pmdas/trace/Makefile.proto b/src/pmdas/trace/Makefile.proto new file mode 100644 index 0000000..397035f --- /dev/null +++ b/src/pmdas/trace/Makefile.proto @@ -0,0 +1,69 @@ +# +# 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. +# + +SHELL = /bin/sh +CC = cc +JC = javac +F77C = f77 +F90C = f90 + +TARGETS = app1 app2 app3 fapp1.f77 fapp1.f90 pmtrace japp1.class + +CDEMOS = app1 app2 app3 pmtrace +F77DEMO = fapp1.f77 +F90DEMO = fapp1.f90 +JDEMO = japp1.class + +CFLAGS = -DPMTRACE_DEBUG -I$(PCP_INC_DIR) +FFLAGS = +JFLAGS = + +default: $(CDEMOS) +fortran77: $(F77DEMO) +fortran90: $(F90DEMO) +java: $(JDEMO) + +pmtrace: pmtrace.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ pmtrace.c -lpcp -lpcp_trace + +app1: app1.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ app1.c -lpcp -lpcp_trace + +app2: app2.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ app2.c -lpcp -lpcp_trace + +app3: app3.c + rm -f $@ + $(CC) $(CFLAGS) -o $@ app3.c -lpcp -lpcp_trace PTHREAD_LIB DLOPEN_LIB MATH_LIB + +fapp1.77: fapp1.f + rm -f $@ + $(F77C) $(FFLAGS) -o $@ fapp1.f -lpcp -lpcp_trace + +fapp1.90: fapp1.f + rm -f $@ + $(F90C) $(FFLAGS) -o $@ fapp1.f -lpcp -lpcp_trace + +japp1.class: japp1.java + rm -f $@ + $(JC) $(JFLAGS) japp1.java + +clean: + rm -f *.o + +clobber: clean + rm -f $(TARGETS) diff --git a/src/pmdas/trace/README b/src/pmdas/trace/README new file mode 100644 index 0000000..0acc1a7 --- /dev/null +++ b/src/pmdas/trace/README @@ -0,0 +1,62 @@ +Trace PMDA +========== + +This PMDA exports application-level transaction and event statistics. + +The PMDA needs to be used in conjunction with the pcp_trace library, +which provides the pmtracebegin, pmtraceend, pmtracepoint, ... functions +to user-level programs. + +For information about the use of the pcp_trace dynamic library and its +function call interface, see pmdatrace(1) and pmdatrace(3). Also, the +example programs - $PCP_VAR_DIR/demos/trace/*.c are useful as starting +points. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT trace + +Installation +============ + + + # cd $PCP_PMDAS_DIR/trace + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + If you choose the "default" installation, appropriate values will + be assigned to those parameters that control the customization of + the PMDA. Otherwise consult the pmdatrace(1) man page for a + description of the customization parameters. + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/trace + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/trace.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/trace/README.demos b/src/pmdas/trace/README.demos new file mode 100644 index 0000000..15227a6 --- /dev/null +++ b/src/pmdas/trace/README.demos @@ -0,0 +1,71 @@ +sample pcp_trace applications +============================= + +pmtrace + is a sample application that uses the pcp_trace interface to send +trace data to the trace PMDA (Performance Metrics Domain Agent). + +The binary is shipped as part of pcp and should be installed in +$PCP_BIN_DIR/pmtrace. A pmtrace(1) man page is available. + +The source is shipped as part of pcp as well and is installed in +$PCP_DEMOS_DIR/trace. If you have the C compiler installed, the +source and Makefile in this directory may be used to create a +functionally equivalent binary, simply by entering the command + + % make pmtrace + +The source in pmtrace.c demonstrates many of the trace services. + + +The C interface ( pmtrace.c, app1.c, app2.c, and app3.c ) +=============== + The default Makefile rules build the C applications only, so +these applications can be built simply by using the command + + % make + + +The Fortran Interface ( fapp1.f ) +===================== + To build the sample Fortran program, using either the f77 or +f90 compilers, use one of these commands + + % make fortran77 + % make fortran90 + + +The Java Interface ( japp1.java ) +================== + To build the sample Java program, and provided you have the +java compiler installed, use the command + + % make java + + Setting the environment variable $CLASSPATH to include the full +path to the trace.class file (/usr/java/classes/com/sgi/pcp) allows +the application to compile and run successfuly. +To run the demo application, after compilation type + + % java japp1 + +which passes the compiled class file into the java interpreter for +subsequent execution. + + +The pcp_trace "stub" library +============================ + To ensure that applications linked with the pcp_trace library are +not locked into being SGI-specific, a "stub" library which has all of +the pcp_trace entry points defined and simple debug switching enabled, +is provided (stub.c). This shared library can be built using + + % make -f Makefile.stub + +and is intended to be simple to port to other platforms. + + +Related manual pages +==================== + pmdatrace(1), pmtrace(1), and pmdatrace(3). + diff --git a/src/pmdas/trace/Remove b/src/pmdas/trace/Remove new file mode 100644 index 0000000..a0fa0a3 --- /dev/null +++ b/src/pmdas/trace/Remove @@ -0,0 +1,38 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Remove the Trace PMDA +# + +# Get standard environment +. $PCP_DIR/etc/pcp.env + +# Get the common procedures and variable assignments +# +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +# The name of the PMDA +# +iam=trace + +# Do it +# +pmdaSetup +pmdaRemove + +exit 0 diff --git a/src/pmdas/trace/app1.c b/src/pmdas/trace/app1.c new file mode 100644 index 0000000..d08daf2 --- /dev/null +++ b/src/pmdas/trace/app1.c @@ -0,0 +1,97 @@ +/* + * 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 + */ + +/* + * app1.c + * + * Simple program to demonstrate use of the PCP trace performance metrics + * domain agent (PMDA(3)). This agent needs to be installed before metrics + * can be made available via the performance metrics namespace (PMNS(4)), + * and the Performance Metrics Collector Daemon (PMCD(1)). + * + * Once this program is running, the trace PMDA metrics & instances can be + * viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and + * pmview(1). To view the help text associated with each of these metrics, + * use: + * $ pminfo -tT trace + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <pcp/trace.h> + + +int +main(int argc, char **argv) +{ + int sts; + char *prog; + + prog = argv[0]; + sts = pmtracestate(PMTRACE_STATE_API|PMTRACE_STATE_COMMS|PMTRACE_STATE_PDU); + fprintf(stderr, "%s: start: %s (state=0x%x)\n", prog, + pmtraceerrstr(0), sts); /* force call to all library symbols */ + + if ((sts = pmtracebegin("simple")) < 0) { + fprintf(stderr, "%s: pmtracebegin error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + if (sleep(2) != 0) { + fprintf(stderr, "%s: sleep prematurely awaken\n", prog); + pmtraceabort("simple"); + } + if ((sts = pmtraceend("simple")) < 0) { + fprintf(stderr, "%s: pmtraceend error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtracebegin("ascanbe")) < 0) { + fprintf(stderr, "%s: pmtracebegin error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + sleep(1); + if ((sts = pmtraceend("ascanbe")) < 0) { + fprintf(stderr, "%s: pmtraceend error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtraceobs("observe", 101.0)) < 0) { + fprintf(stderr, "%s: pmtraceobs error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtracecounter("counter", 101.1)) < 0) { + fprintf(stderr, "%s: pmtracecounter error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + if ((sts = pmtracepoint("imouttahere")) < 0) { + fprintf(stderr, "%s: pmtracepoint error: %s\n", + prog, pmtraceerrstr(sts)); + exit(1); + } + + exit(0); +} diff --git a/src/pmdas/trace/app2.c b/src/pmdas/trace/app2.c new file mode 100644 index 0000000..540c4ce --- /dev/null +++ b/src/pmdas/trace/app2.c @@ -0,0 +1,163 @@ +/* + * 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 + */ + +/* + * app2.c + * + * Sample program to demonstrate use of the PCP trace performance metrics + * domain agent (PMDA(3)). This agent needs to be installed before metrics + * can be made available via the performance metrics namespace (PMNS(4)), + * and the Performance Metrics Collector Daemon (PMCD(1)). + * + * Once this program is running, the trace PMDA metrics & instances can be + * viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and + * pmview(1). To view the help text associated with each of these metrics, + * use: + * $ pminfo -tT trace + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <string.h> +#include <pcp/trace.h> + + +#define IO_UPPER_LIMIT 1000 /* I/O ops */ +#define CPU_UPPER_LIMIT 0xffff /* iterations */ +#define TIME_UPPER_LIMIT 10 /* seconds */ + +static void io_sucker(void); +static void cpu_sucker(void); +static void time_sucker(void); +static char *prog; + +int +main(int argc, char **argv) +{ + int i, sts; + + prog = argv[0]; + srand48(time(0)); + /* uncomment this for debugging information */ + /* pmtracestate(PMTRACE_STATE_API|PMTRACE_STATE_COMMS|PMTRACE_STATE_PDU); */ + /* uncomment this to use the asynchronous protocol */ + /* pmtracestate(PMTRACE_STATE_ASYNC); */ + + for (i = 0;; i++) { + if ((sts = pmtracepoint("mainloop")) < 0) { + fprintf(stderr, "%s: mainloop point trace failed (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + exit(1); + } + switch(i % 3) { + case 0: + time_sucker(); + break; + case 1: + io_sucker(); + break; + case 2: + cpu_sucker(); + break; + } + } +} + + +static void +cpu_sucker(void) +{ + int i, j, sts; + double array[100]; + long iterations; + + if ((sts = pmtracebegin("cpu_sucker")) < 0) { + fprintf(stderr, "%s: cpu_sucker begin (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } + + iterations = lrand48() % CPU_UPPER_LIMIT; + memset((void *)array, 0, 100*sizeof(double)); + + for (i = 0; i < iterations; i++) + for (j = 0; j < 100; j++) + array[j] = (double)(j*iterations); + + if ((sts = pmtraceend("cpu_sucker")) < 0) { + fprintf(stderr, "%s: cpu_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } +} + +static void +time_sucker(void) +{ + long seconds; + int sts; + + if ((sts = pmtracebegin("time_sucker")) < 0) { + fprintf(stderr, "%s: time_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } + + seconds = lrand48() % TIME_UPPER_LIMIT; + sleep((unsigned int)seconds); + + if ((sts = pmtraceend("time_sucker")) < 0) { + fprintf(stderr, "%s: time_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } +} + +static void +io_sucker(void) +{ + long characters; + FILE *foo; + int i, sts; + + if ((sts = pmtracebegin("io_sucker")) < 0) { + fprintf(stderr, "%s: io_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } + + if ((foo = fopen("/dev/null", "rw")) == NULL) { + fprintf(stderr, "%s: io_sucker can't open /dev/null.\n", prog); + return; + } + + characters = lrand48() % IO_UPPER_LIMIT; + for (i = 0; i < characters; i++) { + fgetc(foo); + fputc('!', foo); + } + fclose(foo); + + if ((sts = pmtraceend("io_sucker")) < 0) { + fprintf(stderr, "%s: io_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return; + } +} diff --git a/src/pmdas/trace/app3.c b/src/pmdas/trace/app3.c new file mode 100644 index 0000000..dc0e9c5 --- /dev/null +++ b/src/pmdas/trace/app3.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 1997-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 + */ + +/* + * app3.c + * + * Parallel program to demonstrate use of the PCP trace performance metrics + * domain agent (PMDA(3)). This agent needs to be installed before metrics + * can be made available via the performance metrics namespace (PMNS(4)), + * and the Performance Metrics Collector Daemon (PMCD(1)). + * + * Once this program is running, the trace PMDA metrics & instances can be + * viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and + * pmview(1). To view the help text associated with each of these metrics, + * use: + * $ pminfo -tT trace + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <pthread.h> +#include <pcp/trace.h> + + +#define IO_UPPER_LIMIT 1000 /* I/O ops */ +#define CPU_UPPER_LIMIT 0xffff /* iterations */ +#define TIME_UPPER_LIMIT 10 /* seconds */ + +static void * pio_sucker(void *); +static void * pcpu_sucker(void *); +static void * ptime_sucker(void *); +static char *prog; + +int +main(int argc, char **argv) +{ + int i; + pthread_t p[3]; + + prog = argv[0]; + + pthread_create(p, NULL, pio_sucker, NULL); + pthread_create(p+1, NULL, pcpu_sucker, NULL); + pthread_create(p+2, NULL, ptime_sucker, NULL); + + for (i=0; i < 3; i++) { + wait(NULL); + fprintf(stderr, "%s: reaped sproc #%d\n", prog, i); + } + + exit(0); +} + + +static void * +pcpu_sucker(void *dummy) +{ + int i, j, loops, sts; + double array[100]; + long iterations; + + for (loops = 0; loops < 10; loops++) { + if ((sts = pmtracebegin("pcpu_sucker")) < 0) { + fprintf(stderr, "%s: pcpu_sucker begin (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + + iterations = lrand48() % CPU_UPPER_LIMIT; + memset((void *)array, 0, 100*sizeof(double)); + + for (i = 0; i < iterations; i++) + for (j = 0; j < 100; j++) + array[j] = (double)(j*iterations); + + if ((sts = pmtraceend("pcpu_sucker")) < 0) { + fprintf(stderr, "%s: pcpu_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + } + fprintf(stderr, "%s: finished %d cpu-bound iterations.\n", prog, loops); + return NULL; +} + +static void * +ptime_sucker(void *dummy) +{ + long seconds; + int loops, sts; + + for (loops = 0; loops < 10; loops++) { + if ((sts = pmtracebegin("ptime_sucker")) < 0) { + fprintf(stderr, "%s: ptime_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + + seconds = lrand48() % TIME_UPPER_LIMIT; + sleep((unsigned int)seconds); + + if ((sts = pmtraceend("ptime_sucker")) < 0) { + fprintf(stderr, "%s: ptime_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + } + fprintf(stderr, "%s: finished %d timer iterations.\n", prog, loops); + return NULL; +} + +static void * +pio_sucker(void *dummy) +{ + long characters; + FILE *foo; + int i, loops, sts; + + for (loops = 0; loops < 10; loops++) { + if ((sts = pmtracebegin("pio_sucker")) < 0) { + fprintf(stderr, "%s: pio_sucker start (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + + if ((foo = fopen("/dev/null", "rw")) == NULL) { + fprintf(stderr, "%s: pio_sucker can't open /dev/null.\n", prog); + return NULL; + } + + characters = lrand48() % IO_UPPER_LIMIT; + for (i = 0; i < characters; i++) { + fgetc(foo); + fputc('!', foo); + } + fclose(foo); + + if ((sts = pmtraceend("pio_sucker")) < 0) { + fprintf(stderr, "%s: pio_sucker end (%d): %s\n", + prog, sts, pmtraceerrstr(sts)); + return NULL; + } + } + fprintf(stderr, "%s: finished %d io-bound iterations.\n", prog, loops); + return NULL; +} diff --git a/src/pmdas/trace/fapp1.f b/src/pmdas/trace/fapp1.f new file mode 100644 index 0000000..9fc68e8 --- /dev/null +++ b/src/pmdas/trace/fapp1.f @@ -0,0 +1,102 @@ + program fapp1 + +C Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +C +C This program is free software; you can redistribute it and/or modify it +C under the terms of the GNU General Public License as published by the +C Free Software Foundation; either version 2 of the License, or (at your +C option) any later version. +C +C This program is distributed in the hope that it will be useful, but +C WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +C or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +C for more details. +C +C You should have received a copy of the GNU General Public License along +C with this program; if not, write to the Free Software Foundation, Inc., +C 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +C fapp1.f +C +C Simple program to demonstrate use of the PCP trace performance metrics +C domain agent (PMDA(3)). This agent needs to be installed before metrics +C can be made available via the performance metrics namespace (PMNS(4)), +C and the Performance Metrics Collector Daemon (PMCD(1)). +C +C Once this program is running, the trace PMDA metrics & instances can be +C viewed through PCP monitor tools such as pmchart(1), pmgadgets(1), and +C pmview(1). To view the help text associated with each of these metrics, +C use: +C $ pminfo -tT trace +C +C The pmtracestate constants are defined in /usr/include/pcp/trace.h +C + external pmtracebegin, pmtraceend, pmtracepoint, pmtraceerrstr, pmtracestate + integer pmtracebegin, pmtraceend, pmtracepoint + integer sts + integer debug + character*5 prog + character*40 emesg + real*8 value + integer dbg_noagent, dbg_api, dbg_comms, dbg_pdu + parameter (dbg_noagent = 1, dbg_api = 2, dbg_comms = 4, dbg_pdu = 8) + + prog='fapp1' + +C Addition below is the equivalent to the C 'logical or' operator as +C trace API constants are all disjoint and the high bit is never set. + debug = (dbg_api + dbg_comms + dbg_pdu) + call pmtracestate(debug) + + sts = pmtracebegin('simple') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracebegin error: ',emesg + stop 1 + endif + call sleep(2) + sts = pmtraceend('simple') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtraceend error: ',emesg + stop 1 + endif + + sts = pmtracebegin('ascanbe') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracebegin error: ',emesg + stop 1 + endif + call sleep(1) + sts = pmtraceend('ascanbe') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtraceend error: ',emesg + stop 1 + endif + + sts = pmtracepoint('imouttahere') + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracepoint error: ',emesg + stop 1 + endif + + value = 340.5 + sts = pmtraceobs('end point', value) + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtraceobs error: ',emesg + stop 1 + endif + + value = 340.6 + sts = pmtracecounter('new end point', value) + if (sts .lt. 0) then + call pmtraceerrstr(sts, emesg) + print *,prog,': pmtracecounter error: ',emesg + stop 1 + endif + + end diff --git a/src/pmdas/trace/help b/src/pmdas/trace/help new file mode 100644 index 0000000..d753046 --- /dev/null +++ b/src/pmdas/trace/help @@ -0,0 +1,156 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# trace PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ TRACE.0 Instance domain "trace tag name" for trace PMDA +There is one instance for each transaction which has been seen by +the trace PMDA during the reporting period. + +@ trace.transact.count count by transaction +Count by transaction tag of transactions serviced since the trace +PMDA was started. + +@ trace.transact.rate transaction completion rate over time period +The rate at which each transaction is completed, calculated over the +trace PMDAs aggregation period. + +The exported value is equal to the count of transactions completed +during the aggregation interval, divided by the aggregation interval. + +@ trace.transact.ave_time average transaction time over time period +The average time taken to complete each transaction, calculated over +the trace PMDAs aggregation period. + +The exported value is equal to the sum of the transaction completion +times seen during the aggregation interval divided by the number of +transactions completed during the aggregation interval. + +@ trace.transact.max_time maximum time per transaction +Maximum recorded service time per transaction tag within the current +reporting period. + +@ trace.transact.min_time minimum time per transaction +Minimum recorded service time per transaction tag within the current +reporting period. + +@ trace.transact.total_time total time per transaction +Cumulative time spent processing each transaction since the trace PMDA +was started. + +@ trace.point.count count of point function executions +Count by point tag of marked application points serviced since the +trace PMDA was started. + +@ trace.point.rate point rate over time period +The rate at which execution points are completed, calculated over the +aggregation period in use by the trace PMDA. + +The exported value is equal to the count of successful pmtracepoint(3) +calls made during the aggregation interval, divided by the aggregation +interval. + +@ trace.counter.count count of counter values received +Count, by counter tag, of `counter' values received since the trace +PMDA was started. + +@ trace.counter.rate counter value received rate over time period +The rate at which execution counters are received, calculated over +the aggregation period in use by the trace PMDA. + +The exported value is equal to the count of pmtracecounter(3) calls made +during the aggregation interval, divided by the aggregation interval. + +@ trace.counter.value counter value at last observation +The numeric counter value associated with the last seen counter tag, +since the trace PMDA was started. + +@ trace.observe.count count of observations +Count, by observation tag, of application `observe' points serviced +since the trace PMDA was started. + +@ trace.observe.rate observation rate over time period +The rate at which execution observations are completed, calculated +over the aggregation period in use by the trace PMDA. + +The exported value is equal to the count of pmtraceobs(3) calls made +during the aggregation interval, divided by the aggregation interval. + +@ trace.observe.value value at last observation +The numeric value associated with the last seen observation, since +the trace PMDA was started. + +@ trace.control.period reporting time period +Time (in seconds) over which trace performance data will be gathered. +Any transaction or point trace data seen by the trace PMDA during the +period will be included in the set of exported values. + +@ trace.control.interval update interval within time period +The update interval (within the overall period) at which the current +working set of performance data maintained within the trace PMDA will +be switched into the set of historical data buffers, which are then +used to calculate the exported performance metric values. +This value is directly calculated from the overall time period and the +number of buckets of historical data being maintained within the trace +PMDA. + +For example, if the overall period is 60 seconds and the number of +historical data buckets being maintained is 12, then the buffers will +be rotated once every 5 seconds. + +@ trace.control.buckets number of historical buffers +The number of buffers of historical data maintained by the trace PMDA. +Each bucket contains all transaction and event performance data seen +over a set time interval within the overall reporting time period. + +@ trace.control.port port number for client connections +The TCP/IP port number which the trace PMDA is waiting for client +connections on. + +This has been set as either the default (4322), via the command line, +or via the PMDA_TRACE_PORT environment variable when the trace PMDA +was started. + +@ trace.control.reset clear all tags known to the trace PMDA +Storing any value into this metric with pmstore(1) will cause the trace +PMDA to clear all tags for all metrics from its historical buffers and +begin afresh. + +This is most useful when the instance domains of the trace metrics have +become cluttered with unwanted instances, and the instance domains need +to be refreshed without restarting pmcd(1). + +@ trace.control.debug set the debug level in the trace PMDA +Storing values into this metric with pmstore(1) allows the level of +diagnostic output from the trace PMDA to be controlled. + +By default, the diagnostic output will be written to the file +$PCP_LOG_DIR/pmcd/trace.log. diff --git a/src/pmdas/trace/japp1.java b/src/pmdas/trace/japp1.java new file mode 100644 index 0000000..8c557d4 --- /dev/null +++ b/src/pmdas/trace/japp1.java @@ -0,0 +1,46 @@ +// +// 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 +// + +public class japp1 { + public static void main(String[] args) { + int sts; + trace pcp = new trace(); + + pcp.pmtracestate(pcp.PMTRACE_STATE_API); + + sts = pcp.pmtracebegin("transacting in java"); + if (sts < 0) + System.out.println("pmtracebegin error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtraceend("transacting in java"); + if (sts < 0) + System.out.println("pmtraceend error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtracepoint("java point"); + if (sts < 0) + System.out.println("pmtracepoint error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtraceobs("observing from java", 789.034018); + if (sts < 0) + System.out.println("pmtraceobs error: " + pcp.pmtraceerrstr(sts)); + + sts = pcp.pmtracecounter("counter from java", 789.034019); + if (sts < 0) + System.out.println("pmtracecounter error: " + pcp.pmtraceerrstr(sts)); + } +} diff --git a/src/pmdas/trace/pmns b/src/pmdas/trace/pmns new file mode 100644 index 0000000..c7ef847 --- /dev/null +++ b/src/pmdas/trace/pmns @@ -0,0 +1,62 @@ +/* + * Metrics for trace PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +trace { + transact + point + observe + counter + control +} + +trace.transact { + count TRACE:0:0 + rate TRACE:0:1 + ave_time TRACE:0:2 + min_time TRACE:0:3 + max_time TRACE:0:4 + total_time TRACE:0:5 +} + +trace.point { + count TRACE:0:6 + rate TRACE:0:7 +} + +trace.observe { + count TRACE:0:8 + rate TRACE:0:9 + value TRACE:0:10 +} + +trace.control { + period TRACE:0:11 + interval TRACE:0:12 + buckets TRACE:0:13 + port TRACE:0:14 + reset TRACE:0:15 + debug TRACE:0:16 +} + +trace.counter { + count TRACE:0:17 + rate TRACE:0:18 + value TRACE:0:19 +} diff --git a/src/pmdas/trace/root b/src/pmdas/trace/root new file mode 100644 index 0000000..1ba8c5a --- /dev/null +++ b/src/pmdas/trace/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include <stdpmid> + +root { trace } + +#include "pmns" + diff --git a/src/pmdas/trace/src/GNUmakefile b/src/pmdas/trace/src/GNUmakefile new file mode 100644 index 0000000..efb5e43 --- /dev/null +++ b/src/pmdas/trace/src/GNUmakefile @@ -0,0 +1,57 @@ +# +# Copyright (c) 2000,2003,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 + +IAM = trace +DOMAIN = TRACE +CMDTARGET = pmdatrace$(EXECSUFFIX) +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +CFILES = trace.c client.c comms.c data.c pmda.c +HFILES = data.h client.h comms.h + +LCFLAGS = -I$(TOPDIR)/src/libpcp_trace/src +LLDFLAGS = -L$(TOPDIR)/src/libpcp_trace/src +LLDLIBS = -lpcp_trace $(PCP_PMDALIB) + +LDIRT = *.log *.dir *.pag domain.h $(TARGETS) + +default: build-me + +include $(BUILDRULES) + +ifneq "$(TARGET_OS)" "mingw" +build-me: $(CMDTARGET) + +install: build-me + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(CMDTARGET) $(PMDADIR)/$(CMDTARGET) + $(INSTALL) -m 644 domain.h $(PMDADIR)/domain.h +else +build-me: +install: +endif + +$(IAM)$(EXECSUFFIX): $(OBJECTS) + +comms.o trace.o pmda.o: domain.h + +domain.h: ../../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +default_pcp: default + +install_pcp: install diff --git a/src/pmdas/trace/src/client.c b/src/pmdas/trace/src/client.c new file mode 100644 index 0000000..c3224dd --- /dev/null +++ b/src/pmdas/trace/src/client.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2012-2013 Red Hat. + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "trace_dev.h" +#include "client.h" +#include "comms.h" + +extern fd_set fds; + +int nclients; /* number of entries in array */ +int maxfd; /* largest fd currently in use */ +client_t *clients; /* array of clients */ + +#define MIN_CLIENTS_ALLOC 8 +static int clientsize; + +static int newClient(void); + +client_t * +acceptClient(int reqfd) +{ + int i, fd; + __pmSockLen addrlen; + + i = newClient(); + addrlen = __pmSockAddrSize(); + fd = __pmAccept(reqfd, clients[i].addr, &addrlen); + if (fd == -1) { + __pmNotifyErr(LOG_ERR, "acceptClient(%d) accept: %s", + reqfd, netstrerror()); + return NULL; + } + if (fd > maxfd) + maxfd = fd; + FD_SET(fd, &fds); + clients[i].fd = fd; + clients[i].status.connected = 1; + clients[i].status.padding = 0; + clients[i].status.protocol = 1; /* sync */ + return &clients[i]; +} + +static int +newClient(void) +{ + int i, j; + + for (i = 0; i < nclients; i++) + if (!clients[i].status.connected) + break; + + if (i == clientsize) { + clientsize = clientsize ? clientsize * 2 : MIN_CLIENTS_ALLOC; + clients = (client_t *) realloc(clients, sizeof(client_t)*clientsize); + if (clients == NULL) + __pmNoMem("newClient", sizeof(client_t)*clientsize, PM_FATAL_ERR); + for (j = i; j < clientsize; j++) + clients[j].addr = NULL; + } + clients[i].addr = __pmSockAddrAlloc(); + if (clients[i].addr == NULL) + __pmNoMem("newClient", __pmSockAddrSize(), PM_FATAL_ERR); + if (i >= nclients) + nclients = i + 1; + return i; +} + +void +deleteClient(client_t *cp) +{ + int i; + + for (i = 0; i < nclients; i++) + if (cp == &clients[i]) + break; + + if (i == nclients) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + __pmNotifyErr(LOG_ERR, "deleteClient: tried to delete non-existent client"); + } +#endif + return; + } + if (cp->fd != -1) { + __pmtracenomoreinput(cp->fd); + FD_CLR(cp->fd, &fds); + close(cp->fd); + } + if (cp->fd == maxfd) { + maxfd = -1; + for (i = 0; i < nclients; i++) + if (clients[i].fd > maxfd) + maxfd = clients[i].fd; + } + __pmSockAddrFree(cp->addr); + cp->addr = NULL; + cp->status.connected = 0; + cp->status.padding = 0; + cp->status.protocol = 1; /* sync */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "deleteClient: client removed (fd=%d)", cp->fd); +#endif + cp->fd = -1; +} + +void +showClients(void) +{ + int i; + + fprintf(stderr, "%s: %d connected clients:\n", pmProgname, nclients); + fprintf(stderr, " fd type conn client connection from\n" + " == ===== ==== ======================\n"); + for (i=0; i < nclients; i++) { + char *hostName; + char *hostAddr; + + fprintf(stderr, " %3d", clients[i].fd); + fprintf(stderr, " %s ", clients[i].status.protocol == 1 ? "sync ":"async"); + fprintf(stderr, "%s ", clients[i].status.connected == 1 ? "up ":"down"); + hostName = __pmGetNameInfo(clients[i].addr); + if (hostName == NULL) { + hostAddr = __pmSockAddrToString(clients[i].addr); + fprintf(stderr, "%s", hostAddr); + free(hostAddr); + } else { + fprintf(stderr, "%-40.40s", hostName); + free(hostName); + } + if (clients[i].denyOps != 0) { + fprintf(stderr, " "); + if (clients[i].denyOps & TR_OP_SEND) + fprintf(stderr, "send "); + } + + fputc('\n', stderr); + } + fputc('\n', stderr); +} diff --git a/src/pmdas/trace/src/client.h b/src/pmdas/trace/src/client.h new file mode 100644 index 0000000..9cb3ad2 --- /dev/null +++ b/src/pmdas/trace/src/client.h @@ -0,0 +1,38 @@ +/* + * 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 + */ + +#ifndef CLIENT_H +#define CLIENT_H + +typedef struct { + int fd; /* socket descriptor */ + __pmSockAddr *addr; /* address of client */ + struct { /* connection status */ + unsigned int connected : 1; /* client connected */ + unsigned int version : 8; /* client pdu version */ + unsigned int protocol : 1; /* synchronous or not */ + unsigned int padding :22; /* currently unused */ + } status; + unsigned int denyOps; +} client_t; + +extern client_t *acceptClient(int); +extern void deleteClient(client_t *); +extern void showClients(void); + +#endif /* CLIENT_H */ diff --git a/src/pmdas/trace/src/comms.c b/src/pmdas/trace/src/comms.c new file mode 100644 index 0000000..4432c05 --- /dev/null +++ b/src/pmdas/trace/src/comms.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2012-2013 Red Hat. All Rights Reserved. + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "trace.h" +#include "trace_dev.h" +#include "domain.h" +#include "client.h" +#include "comms.h" + +extern struct timeval interval; +extern int readData(int, int *); +extern void timerUpdate(void); + +extern int maxfd; +extern int nclients; +extern client_t *clients; +extern int ctlport; /* control port number */ +static int ctlfd; /* fd for control port */ +static int pmcdfd; /* fd for pmcd */ + +void alarming(int, void *); +static void hangup(int); +static int getcport(void); + +/* currently in-use fd mask */ +fd_set fds; + +/* the AF event number */ +int afid = -1; + +void +traceMain(pmdaInterface *dispatch) +{ + client_t *cp; + fd_set readyfds; + int nready, i, pdutype, sts, protocol; + + ctlfd = getcport(); + pmcdfd = __pmdaInFd(dispatch); + maxfd = (ctlfd > pmcdfd) ? (ctlfd):(pmcdfd); + FD_ZERO(&fds); + FD_SET(ctlfd, &fds); + FD_SET(pmcdfd, &fds); + + signal(SIGHUP, hangup); + + /* arm interval timer */ + if ((afid = __pmAFregister(&interval, NULL, alarming)) < 0) { + __pmNotifyErr(LOG_ERR, "error registering asynchronous event handler"); + exit(1); + } + + for (;;) { + memcpy(&readyfds, &fds, sizeof(readyfds)); + nready = select(maxfd+1, &readyfds, NULL, NULL, NULL); + + if (nready == 0) + continue; + else if (nready < 0) { + if (neterror() != EINTR) { + __pmNotifyErr(LOG_ERR, "select failure: %s", netstrerror()); + exit(1); + } + continue; + } + + __pmAFblock(); + if (FD_ISSET(pmcdfd, &readyfds)) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "processing pmcd request [fd=%d]", pmcdfd); +#endif + if (__pmdaMainPDU(dispatch) < 0) { + __pmAFunblock(); + exit(1); /* fatal if we lose pmcd */ + } + } + /* handle request on control port */ + if (FD_ISSET(ctlfd, &readyfds)) { + if ((cp = acceptClient(ctlfd)) != NULL) { + sts = __pmAccAddClient(cp->addr, &cp->denyOps); + if (sts == PM_ERR_PERMISSION) + sts = PMTRACE_ERR_PERMISSION; + else if (sts == PM_ERR_CONNLIMIT) + sts = PMTRACE_ERR_CONNLIMIT; + else if (sts >= 0) + sts = TRACE_PDU_VERSION; + __pmtracesendack(cp->fd, sts); + if (sts < 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(cp->addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: connect refused, denyOps=0x%x: %s", + hostAddr, cp->fd, cp->denyOps, pmtraceerrstr(sts)); + free(hostAddr); + } +#endif + deleteClient(cp); + } + else { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(cp->addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: new connection, denyOps=0x%x", + hostAddr, cp->fd, cp->denyOps); + free(hostAddr); + } +#endif + ; + } + } + } + for (i = 0; i < nclients; i++) { + if (!clients[i].status.connected) + continue; + if (clients[i].denyOps & TR_OP_SEND) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: send denied, denyOps=0x%x", + hostAddr, clients[i].fd, clients[i].denyOps); + free(hostAddr); + } +#endif + __pmtracesendack(clients[i].fd, PMTRACE_ERR_PERMISSION); + __pmAccDelClient(clients[i].addr); + deleteClient(&clients[i]); + } + else if (FD_ISSET(clients[i].fd, &readyfds)) { + protocol = 1; /* default to synchronous */ + do { + if ((pdutype = readData(clients[i].fd, &protocol)) < 0) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: close connection", + hostAddr, clients[i].fd); + free(hostAddr); + } +#endif + __pmAccDelClient(clients[i].addr); + deleteClient(&clients[i]); + } + else { + clients[i].status.protocol = protocol; + if (clients[i].status.connected) { +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_DEBUG, "client %s [fd=%d]: %s ACK (type=%d)", + hostAddr, clients[i].fd, + protocol ? "sending" : "no", pdutype); + free(hostAddr); + } +#endif + if (protocol == 1) { + sts = __pmtracesendack(clients[i].fd, pdutype); + if (sts < 0) { + char *hostAddr = __pmSockAddrToString(clients[i].addr); + __pmNotifyErr(LOG_ERR, "client %s [fd=%d]: ACK send failed (type=%d): %s", + hostAddr, clients[i].fd, + pdutype, pmtraceerrstr(sts)); + free(hostAddr); + } + } + } + } + } while (__pmtracemoreinput(clients[i].fd)); + } + } + __pmAFunblock(); + } +} + + +void +alarming(int sig, void *ptr) +{ + timerUpdate(); +} + +static void +hangup(int sig) +{ + showClients(); + signal(SIGHUP, hangup); +} + +/* + * Create socket for incoming connections and bind to it an address for + * clients to use. Only returns if it succeeds (exits on failure). + */ +static int +getcport(void) +{ + int fd; + int i=1, one=1, sts; + struct sockaddr_in myAddr; + struct linger noLinger = {1, 0}; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + __pmNotifyErr(LOG_ERR, "getcport: socket: %s", netstrerror()); + exit(1); + } + /* avoid 200 ms delay */ + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &i, + (__pmSockLen)sizeof(i)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(nodelay): %s", + netstrerror()); + exit(1); + } + /* don't linger on close */ + if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &noLinger, + (__pmSockLen)sizeof(noLinger)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(nolinger): %s", + netstrerror()); + exit(1); + } +#ifndef IS_MINGW + /* ignore dead client connections */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, + (__pmSockLen)sizeof(one)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(reuseaddr): %s", + netstrerror()); + exit(1); + } +#else + /* see MSDN tech note: "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" */ + if (setsockopt(sfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &one, + (__pmSockLen)sizeof(one)) < 0) { + __pmNotifyErr(LOG_ERR, "getcport: setsockopt(excladdruse): %s", + netstrerror()); + exit(1); + } +#endif + + if (ctlport == -1) { + /* + * check for port info in the environment + */ + char *env_str; + if ((env_str = getenv(TRACE_ENV_PORT)) != NULL) { + char *end_ptr; + + ctlport = (int)strtol(env_str, &end_ptr, 0); + if (*end_ptr != '\0' || ctlport < 0) { + __pmNotifyErr(LOG_WARNING, "env port is bogus (%s)", env_str); + ctlport = TRACE_PORT; + } + } + else + ctlport = TRACE_PORT; + } + + /* TODO: IPv6 */ + memset(&myAddr, 0, sizeof(myAddr)); + myAddr.sin_family = AF_INET; + myAddr.sin_addr.s_addr = htonl(INADDR_ANY); + myAddr.sin_port = htons(ctlport); + + sts = bind(fd, (const struct sockaddr *)&myAddr, sizeof(myAddr)); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "bind(%d): %s", ctlport, netstrerror()); + exit(1); + } + sts = listen(fd, 5); /* Max. of 5 pending connection requests */ + if (sts == -1) { + __pmNotifyErr(LOG_ERR, "listen: %s", netstrerror()); + exit(1); + } + + return fd; +} diff --git a/src/pmdas/trace/src/comms.h b/src/pmdas/trace/src/comms.h new file mode 100644 index 0000000..b564f4d --- /dev/null +++ b/src/pmdas/trace/src/comms.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 1997-2001 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef COMMS_H +#define COMMS_H + +#define TR_OP_NONE 0x0 +#define TR_OP_SEND 0x1 +#define TR_OP_ALL 0x1 + +#endif /* COMMS_H */ diff --git a/src/pmdas/trace/src/data.c b/src/pmdas/trace/src/data.c new file mode 100644 index 0000000..2db8fb4 --- /dev/null +++ b/src/pmdas/trace/src/data.c @@ -0,0 +1,122 @@ +/* + * 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 + */ + +#include "pmapi.h" +#include "impl.h" +#include "data.h" + +int +instcmp(void *a, void *b) +{ + instdata_t *aa = (instdata_t *)a; + instdata_t *bb = (instdata_t *)b; + + if (aa == NULL || bb == NULL) + return 0; + if (aa->type != bb->type) + return 0; + return !strcmp(aa->tag, bb->tag); +} + +void +instdel(void *a) +{ + instdata_t *k = (instdata_t *)a; + + if (k != NULL) { + if (k->tag != NULL) + free(k->tag); + free(k); + } +} + +void +instprint(__pmHashTable *t, void *e) +{ + instdata_t *i = (instdata_t *)e; + + __pmNotifyErr(LOG_DEBUG, "Instance history table entry\n" + "Name: '%s'\n type: %d\n inst: %d\n", + i->tag, i->type, i->instid); +} + + +int +datacmp(void *a, void *b) +{ + hashdata_t *aa = (hashdata_t *)a; + hashdata_t *bb = (hashdata_t *)b; + + if (aa == NULL || bb == NULL) + return 0; + if (aa->tracetype != bb->tracetype) + return 0; + return !strcmp(aa->tag, bb->tag); +} + +void +datadel(void *a) +{ + hashdata_t *k = (hashdata_t *)a; + + if (k != NULL) { + if (k->tag != NULL) + free(k->tag); + free(k); + } +} + +void +dataprint(__pmHashTable *t, void *e) +{ + hashdata_t *h = (hashdata_t *)e; + + __pmNotifyErr(LOG_DEBUG, "PMDA hash table entry\n" + "Name: '%s'\n filedes: %d\n" + " type: %d\n length: %d\n" + " padding: %d\n count: %d\n" + " txmin: %f\n txmax: %f\n" + " txsum: %f\nSize: %d\n" + "-----------\n", + h->tag, h->fd, h->tracetype, h->taglength, h->padding, + (int)h->txcount, h->txmin, h->txmax, h->txsum, (int)sizeof(*h)); +} + + +#ifdef PCP_DEBUG +void +debuglibrary(int flag) +{ + extern int __pmstate; + int state; + + state = pmtracestate(0); + if (flag & DBG_TRACE_APPL0) + state |= PMTRACE_STATE_COMMS; + if (flag & DBG_TRACE_PDU) + state |= PMTRACE_STATE_PDU; + if (flag & DBG_TRACE_PDUBUF) + state |= PMTRACE_STATE_PDUBUF; + if (flag == 0) + __pmstate = 0; + else + pmtracestate(state); +} +#endif + + diff --git a/src/pmdas/trace/src/data.h b/src/pmdas/trace/src/data.h new file mode 100644 index 0000000..e4c0352 --- /dev/null +++ b/src/pmdas/trace/src/data.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef TRACE_DATA_H +#define TRACE_DATA_H + +#include "hash.h" +#include "trace.h" +#include "trace_dev.h" + +typedef struct { + char *tag; + unsigned int type; + unsigned int instid; +} instdata_t; + +typedef struct __pmHashTab hashtable_t; + +int instcmp(void *a, void *b); +void instdel(void *a); +void instprint(hashtable_t *t, void *e); + +typedef struct { + char *tag; + unsigned int id; + int fd; + unsigned int tracetype : 8; + unsigned int taglength : 8; + unsigned int padding : 16; + __uint64_t realcount; /* real total seen by the PMDA */ + double realtime; /* total time for transactions */ + __int32_t txcount; /* count this interval or -1 */ + double txmin; /* minimum value this interval */ + double txmax; /* maximum value this interval */ + double txsum; /* summed across the interval */ +} hashdata_t; + +int datacmp(void *a, void *b); +void datadel(void *a); +void dataprint(hashtable_t *t, void *e); + +typedef struct { + unsigned int numstats : 8; /* number of entries in this table */ + unsigned int working : 1; /* this the current working table? */ + hashtable_t *stats; +} statlist_t; + +typedef struct { + statlist_t *ring; /* points to all statistics */ + unsigned int level; /* controls reporting level */ +} ringbuf_t; + +#ifdef PCP_DEBUG +void debuglibrary(int); +#endif + +#endif /* TRACE_DATA_H */ diff --git a/src/pmdas/trace/src/pmda.c b/src/pmdas/trace/src/pmda.c new file mode 100644 index 0000000..36560aa --- /dev/null +++ b/src/pmdas/trace/src/pmda.c @@ -0,0 +1,228 @@ +/* + * Trace PMDA - process level transaction monitoring for libpcp_trace processes + * + * Copyright (c) 2012 Red Hat. + * Copyright (c) 1997-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. + */ + +#include <ctype.h> +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "trace.h" +#include "trace_dev.h" +#include "comms.h" + +#define DEFAULT_TIMESPAN 60 /* one minute */ +#define DEFAULT_BUFSIZE 5 /* twelve second update */ + +struct timeval timespan = { DEFAULT_TIMESPAN, 0 }; +struct timeval interval; +unsigned int rbufsize = DEFAULT_BUFSIZE; +int ctlport = -1; +char *ctlsock; + +static char mypath[MAXPATHLEN]; +static char *username; + +extern void traceInit(pmdaInterface *dispatch); +extern void traceMain(pmdaInterface *dispatch); +extern int updateObserveValue(const char *); +extern int updateCounterValue(const char *); +extern void debuglibrary(int); + +static void +usage(void) +{ + fprintf(stderr, +"Usage: %s [options]\n\ +\n\ +Options:\n\ + -d domain use domain (numeric) for metrics domain of PMDA\n\ + -l logfile write log into logfile rather than using default file\n\ + -A access host based access control\n\ + -I port expect programs to connect on given inet port (number/name)\n\ + -M username user account to run under (default \"pcp\")\n\ + -N buckets number of historical data buffers maintained\n\ + -T period time over which samples are considered (default 60 seconds)\n\ + -U units export observation values using the given units\n\ + -V units export counter values using the given units\n", + pmProgname); + exit(1); +} + +static char * +squash(char *str, int *offset) +{ + char *hspec = NULL; + char *p = str; + int i = 0; + + hspec = strdup(str); /* make sure we have space */ + *offset = 0; + while (isspace((int)*p)) { p++; (*offset)++; } + while (p && *p != ':' && *p != '\0') { + hspec[i++] = *p; + p++; + } + hspec[i] = '\0'; + *offset += i; + return hspec; +} + +static int +parseAuth(char *spec) +{ + static int first = 1; + int offset, maxconn, specops = TR_OP_ALL, denyops; + char *p, *endnum; + + if (first) { + if (__pmAccAddOp(TR_OP_SEND) < 0) { + __pmNotifyErr(LOG_ERR, "failed to add send auth operation"); + return -1; + } + first = 0; + } + + if (strncasecmp(spec, "disallow:", 9) == 0) { + p = squash(&spec[9], &offset); + if (p == NULL || p[0] == '\0') { + fprintf(stderr, "%s: invalid disallow (%s)\n", pmProgname, spec); + if (p) + free(p); + return -1; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "deny: host '%s'\n", p); +#endif + denyops = TR_OP_SEND; + if (__pmAccAddHost(p, specops, denyops, 0) < 0) + __pmNotifyErr(LOG_ERR, "failed to add authorisation (%s)", p); + free(p); + } + else if (strncasecmp(spec, "allow:", 6) == 0) { + p = squash(&spec[6], &offset); + if (p == NULL || p[0] == '\0') { + fprintf(stderr, "%s: invalid allow (%s)\n", pmProgname, spec); + if (p) + free(p); + return -1; + } + offset += 7; + maxconn = (int)strtol(&spec[offset], &endnum, 10); + if (*endnum != '\0' || maxconn < 0) { + fprintf(stderr, "%s: bogus max connection in '%s'\n", pmProgname, + &spec[offset]); + free(p); + return -1; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "allow: host '%s', maxconn=%d\n", p, maxconn); +#endif + denyops = TR_OP_NONE; + if (__pmAccAddHost(p, specops, denyops, maxconn) < 0) + __pmNotifyErr(LOG_ERR, "failed to add authorisation (%s)", p); + free(p); + } + else { + fprintf(stderr, "%s: access spec is invalid (%s)\n", pmProgname, spec); + return -1; + } + return 0; +} + +int +main(int argc, char **argv) +{ + pmdaInterface dispatch; + char *endnum; + int err = 0; + int sep = __pmPathSeparator(); + int c = 0; + + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "trace" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, TRACE, + "trace.log", mypath); + + /* need - port, as well as time interval and time span for averaging */ + while ((c = pmdaGetOpt(argc, argv, "A:D:d:I:l:T:M:N:U:V:?", + &dispatch, &err)) != EOF) { + switch(c) { + case 'A': + if (parseAuth(optarg) < 0) + err++; + /* add optarg to access control list */ + break; + case 'I': + ctlport = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0' || ctlport < 0) + ctlsock = optarg; + break; + case 'M': + username = optarg; + break; + case 'N': + rbufsize = (int)strtol(optarg, &endnum, 10); + if (*endnum != '\0' || rbufsize < 1) { + fprintf(stderr, "%s: -N requires a positive number.\n", pmProgname); + err++; + } + break; + case 'T': + if (pmParseInterval(optarg, ×pan, &endnum) < 0) { + fprintf(stderr, "%s: -T requires a time interval: %s\n", + pmProgname, endnum); + free(endnum); + err++; + } + break; + case 'U': + if (updateObserveValue(optarg) < 0) + err++; + break; + case 'V': + if (updateCounterValue(optarg) < 0) + err++; + break; + default: + err++; + } + } + + if (err) + usage(); + + interval.tv_sec = (int)(timespan.tv_sec / rbufsize); + interval.tv_usec = (long)((timespan.tv_sec % rbufsize) * 1000000); + rbufsize++; /* reserve space for the `working' buffer */ + +#ifdef PCP_DEBUG + debuglibrary(pmDebug); +#endif + + pmdaOpenLog(&dispatch); + __pmSetProcessIdentity(username); + traceInit(&dispatch); + pmdaConnect(&dispatch); + traceMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/trace/src/trace.c b/src/pmdas/trace/src/trace.c new file mode 100644 index 0000000..f57c4bb --- /dev/null +++ b/src/pmdas/trace/src/trace.c @@ -0,0 +1,1151 @@ +/* + * Copyright (c) 1997-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. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" +#include "data.h" +#include "trace_dev.h" + +static pmdaIndom indomtab[] = { /* list of trace metric instance domains */ +#define TRANSACT_INDOM 0 + { TRANSACT_INDOM, 0, NULL }, /* dynamically updated */ +#define POINT_INDOM 1 + { POINT_INDOM, 0, NULL }, /* dynamically updated */ +#define OBSERVE_INDOM 2 + { OBSERVE_INDOM, 0, NULL }, /* dynamically updated */ +#define COUNTER_INDOM 3 + { COUNTER_INDOM, 0, NULL }, /* dynamically updated */ +}; + +static int transacts; /* next instance# to allocate */ +static int points; /* next instance# to allocate */ +static int counters; /* next instance# to allocate */ +static int observes; /* next instance# to allocate */ +static int tsortflag; /* need sort on next request? */ +static int psortflag; /* need sort on next request? */ +static int csortflag; /* need sort on next request? */ +static int osortflag; /* need sort on next request? */ + +/* all metrics supported in this PMDA - one table entry for each */ +static pmdaMetric metrictab[] = { +/* transact.count */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U64, TRANSACT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE)} }, +/* transact.rate */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* transact.ave_time */ + { NULL, + { PMDA_PMID(0,2), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,-1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* transact.min_time */ + { NULL, + { PMDA_PMID(0,3), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* transact.max_time */ + { NULL, + { PMDA_PMID(0,4), PM_TYPE_FLOAT, TRANSACT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* transact.total_time */ + { NULL, + { PMDA_PMID(0,5), PM_TYPE_DOUBLE, TRANSACT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* point.count */ + { NULL, + { PMDA_PMID(0,6), PM_TYPE_U64, POINT_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE)} }, +/* point.rate */ + { NULL, + { PMDA_PMID(0,7), PM_TYPE_FLOAT, POINT_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* observe.count */ + { NULL, + { PMDA_PMID(0,8), PM_TYPE_U64, OBSERVE_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE)} }, +/* observe.rate */ + { NULL, + { PMDA_PMID(0,9), PM_TYPE_FLOAT, OBSERVE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE)} }, +/* observe.value */ + { NULL, + { PMDA_PMID(0,10), PM_TYPE_DOUBLE, OBSERVE_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, /* this may be modified at startup */ +/* control.timespan */ + { NULL, + { PMDA_PMID(0,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* control.interval */ + { NULL, + { PMDA_PMID(0,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,1,0, 0,PM_TIME_SEC,0)} }, +/* control.buckets */ + { NULL, + { PMDA_PMID(0,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, +/* control.port */ + { NULL, + { PMDA_PMID(0,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, +/* control.reset */ + { NULL, + { PMDA_PMID(0,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0)} }, +/* control.debug */ + { NULL, + { PMDA_PMID(0,16), PM_TYPE_32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0, 0,0,0) }, }, +/* counter.count */ + { NULL, + { PMDA_PMID(0,17), PM_TYPE_U64, COUNTER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1, 0,0,PM_COUNT_ONE) }, }, +/* counter.rate */ + { NULL, + { PMDA_PMID(0,18), PM_TYPE_FLOAT, COUNTER_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,-1,1, 0,PM_TIME_SEC,PM_COUNT_ONE) }, }, +/* counter.value */ + { NULL, + { PMDA_PMID(0,19), PM_TYPE_DOUBLE, COUNTER_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,0, 0,0,0) }, }, /* this may be modified at startup */ +}; + +extern void __pmdaStartInst(pmInDom indom, pmdaExt *pmda); + +extern int ctlport; +extern unsigned int rbufsize; +extern struct timeval timespan; +extern struct timeval interval; + +static ringbuf_t ringbuf; /* *THE* ring buffer of trace data */ +static hashtable_t summary; /* globals + recent ringbuf summary */ +static hashtable_t history; /* holds every instance seen so far */ +static unsigned int rpos; /* `working' buffer, within ringbuf */ +static unsigned int dosummary = 1; /* summary refreshed this interval? */ +static unsigned int tindomsize = 0; /* updated local to fetch only */ +static unsigned int pindomsize = 0; /* updated local to fetch only */ +static unsigned int oindomsize = 0; /* updated local to fetch only */ +static unsigned int cindomsize = 0; /* updated local to fetch only */ + /* note: {t,p,o,c}indomsize are only valid when dosummary equals zero */ + + +/* allow configuration of trace.observe.value/trace.counter.value units */ +static int +updateValueUnits(const char *str, int offset) +{ + int units[6], i, sts = 0; + char *s, *sptr, *endp; + + if ((sptr = strdup(str)) == NULL) + return -oserror(); + s = sptr; + + for (i = 0; i < 6; i++) { + if ((s = strtok((i==0 ? sptr : NULL), ",")) == NULL) { + fprintf(stderr, "%s: token parse error in string \"%s\"\n", + pmProgname, str); + sts = -1; + goto leaving; + } + units[i] = (int)strtol(s, &endp, 10); + if (*endp) { + fprintf(stderr, "%s: integer parse error for substring \"%s\"\n", + pmProgname, s); + sts = -1; + goto leaving; + } + } + + /* update table entry for this value metric */ + metrictab[offset].m_desc.units.dimSpace = units[0]; + metrictab[offset].m_desc.units.dimTime = units[1]; + metrictab[offset].m_desc.units.dimCount = units[2]; + metrictab[offset].m_desc.units.scaleSpace = units[3]; + metrictab[offset].m_desc.units.scaleTime = units[4]; + metrictab[offset].m_desc.units.scaleCount = units[5]; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "%s: value metric units updated using \"%s\"\n" + "dimSpace=%d, dimTime=%d, dimCount=%d, scaleSpace=%d, " + "scaleTime=%d, scaleCount=%d\n", pmProgname, str, + units[0], units[1], units[2], units[3], units[4], units[5]); + } +#endif + +leaving: + if (sptr != NULL) + free(sptr); + return sts; +} + +int updateObserveValue(const char *str) { return updateValueUnits(str, 10); } +int updateCounterValue(const char *str) { return updateValueUnits(str, 19); } + + +static int +compareInstance(const void *a, const void *b) +{ + pmdaInstid *aa = (pmdaInstid *)a; + pmdaInstid *bb = (pmdaInstid *)b; + return aa->i_inst - bb->i_inst; +} + +/* + * sort reset instance domains to be friendly to pmclients + * (only PMDA knows when optimal time to sort these infrequently-changing sets) + */ +static void +indomSortCheck(void) +{ + if (tsortflag) { + qsort(indomtab[TRANSACT_INDOM].it_set, + indomtab[TRANSACT_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + tsortflag = 0; + } + if (psortflag) { + qsort(indomtab[POINT_INDOM].it_set, + indomtab[POINT_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + psortflag = 0; + } + if (osortflag) { + qsort(indomtab[OBSERVE_INDOM].it_set, + indomtab[OBSERVE_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + osortflag = 0; + } + if (csortflag) { + qsort(indomtab[COUNTER_INDOM].it_set, + indomtab[COUNTER_INDOM].it_numinst, + sizeof(pmdaInstid), compareInstance); + csortflag = 0; + } +} + +/* + * wrapper for pmdaInstance which we need to ensure is called with the + * _sorted_ contents of the instance domain. + */ +static int +traceInstance(pmInDom indom, int foo, char *bar, __pmInResult **iresp, pmdaExt + *pmda) +{ + indomSortCheck(); + return pmdaInstance(indom, foo, bar, iresp, pmda); +} + +/* + * `summary' table deletion may add to the `history' table. + */ +void +summarydel(void *a) +{ + hashdata_t *k = (hashdata_t *)a; + instdata_t check; + + check.tag = k->tag; + check.type = k->tracetype; + check.instid = k->id; + if (__pmhashlookup(&history, check.tag, &check) == NULL) { + if (__pmhashinsert(&history, check.tag, &check) < 0) + __pmNotifyErr(LOG_ERR, "history table insert failure - '%s' " + "instance will not maintain its instance number.", check.tag); + } + if (k != NULL) + free(k); /* don't free k->tag - its in the history table */ +} + +/* + * Processes data from pcp_trace-linked client programs. + * + * Return negative only on fd-related errors, as that connection will + * later be closed. Other errors - report in log file but continue. + */ +int +readData(int clientfd, int *protocol) +{ + __pmTracePDU *result; + double data; + hashdata_t newhash; + hashdata_t *hptr; + hashdata_t hash; + char *tag; + int type, taglen, sts; + int freeflag=0; + + if ((sts = __pmtracegetPDU(clientfd, TRACE_TIMEOUT_NEVER, &result)) < 0) { + __pmNotifyErr(LOG_ERR, "bogus PDU read - %s", pmtraceerrstr(sts)); + return -1; + } + else if (sts == TRACE_PDU_DATA) { + if ((sts = __pmtracedecodedata(result, &tag, &taglen, + &type, protocol, &data)) < 0) + return -1; + if (type < TRACE_FIRST_TYPE || type > TRACE_LAST_TYPE) { + __pmNotifyErr(LOG_ERR, "unknown trace type for '%s' (%d)", tag, type); + free(tag); + return -1; + } + newhash.tag = tag; + newhash.taglength = taglen; + newhash.tracetype = type; + } + else if (sts == 0) { /* client has exited - cleanup in mainloop */ + return -1; + } + else { /* unknown PDU type - bail & later kill connection */ + __pmNotifyErr(LOG_ERR, "unknown PDU - expected data PDU" + " (not type #%d)", sts); + return -1; + } + + /* + * First, update the global summary table with this new data + */ + if ((hptr = __pmhashlookup(&summary, tag, &newhash)) == NULL) { + instdata_t check, *iptr; + int size, index, indom; + + check.tag = newhash.tag; + check.type = newhash.tracetype; + if ((iptr = __pmhashlookup(&history, check.tag, &check)) != NULL) { + newhash.id = iptr->instid; /* reuse pre-reset instance ID */ + if (iptr->type == TRACE_TYPE_TRANSACT) tsortflag++; + else if (iptr->type == TRACE_TYPE_POINT) psortflag++; + else if (iptr->type == TRACE_TYPE_COUNTER) csortflag++; + else /*(iptr->type == TRACE_TYPE_OBSERVE)*/ osortflag++; + } + else if (type == TRACE_TYPE_TRANSACT) + newhash.id = ++transacts; + else if (type == TRACE_TYPE_POINT) + newhash.id = ++points; + else if (type == TRACE_TYPE_COUNTER) + newhash.id = ++counters; + else /* TRACE_TYPE_OBSERVE */ + newhash.id = ++observes; + newhash.txcount = -1; /* first time since reset or start */ + newhash.padding = 0; + newhash.realcount = 1; + newhash.realtime = data; + newhash.fd = clientfd; + newhash.txmin = newhash.txmax = data; + newhash.txsum = data; + hptr = &newhash; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "'%s' is new to the summary table!", + hptr->tag); +#endif + if (__pmhashinsert(&summary, hptr->tag, hptr) < 0) { + __pmNotifyErr(LOG_ERR, "summary table insert failure - '%s' " + "data ignored", hptr->tag); + free(hptr->tag); + return -1; + } + /* also update the instance domain */ + if (hptr->tracetype == TRACE_TYPE_TRANSACT) + indom = TRANSACT_INDOM; + else if (hptr->tracetype == TRACE_TYPE_POINT) + indom = POINT_INDOM; + else if (hptr->tracetype == TRACE_TYPE_COUNTER) + indom = COUNTER_INDOM; + else /*(hptr->tracetype == TRACE_TYPE_OBSERVE)*/ + indom = OBSERVE_INDOM; + +#ifdef DESPERATE + /* walk the indom table - if we find this new tag in it already, then + * something is badly busted. + */ + for (sts = 0; sts < indomtab[indom].it_numinst; sts++) { + if (strcmp(indomtab[indom].it_set[sts].i_name, hptr->tag) == 0) { + fprintf(stderr, "'%s' (inst=%d, type=%d) entry in indomtab already!!!\n", + hptr->tag, indomtab[indom].it_set[sts].i_inst, hptr->tracetype); + abort(); + } + } +#endif + + index = indomtab[indom].it_numinst; + size = (index+1)*(int)sizeof(pmdaInstid); + if ((indomtab[indom].it_set = (pmdaInstid *) + realloc(indomtab[indom].it_set, size)) == NULL) { + __pmNotifyErr(LOG_ERR, "dropping instance '%s': %s", hptr->tag, + osstrerror()); + free(hptr->tag); + return -1; + } + indomtab[indom].it_set[index].i_inst = hptr->id; + indomtab[indom].it_set[index].i_name = hptr->tag; + indomtab[indom].it_numinst++; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + fprintf(stderr, "Updated indom #%d:\n", indom); + for (index=0; index < indomtab[indom].it_numinst; index++) + fprintf(stderr, " Instance %d='%s'\n", + indomtab[indom].it_set[index].i_inst, + indomtab[indom].it_set[index].i_name); + } +#endif + } + else { /* update an existing entry */ + freeflag++; /* wont need tag afterwards, so mark for deletion */ + if (hptr->taglength != newhash.taglength) { + __pmNotifyErr(LOG_ERR, "hash table update failure - '%s' " + "data ignored (bad tag length)", tag); + free(newhash.tag); + return -1; + } + else { /* update existing entries free running counter */ + hptr->realcount++; + if (hptr->tracetype == TRACE_TYPE_TRANSACT) + hptr->realtime += data; + /* keep running total of time attributed to transactions */ + else if (hptr->tracetype == TRACE_TYPE_COUNTER) + hptr->txsum = data; + /* counters are 'permanent' and immediately available */ + else if (hptr->tracetype == TRACE_TYPE_OBSERVE) + hptr->txsum = data; + /* observations are 'permanent' and immediately available */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "'%s' real count updated (%d)", + hptr->tag, (int)hptr->realcount); +#endif + } + } + + /* + * Next, update the current ring buffer entry with new trace data + */ + if ((hptr = __pmhashlookup(ringbuf.ring[rpos].stats, tag, + &newhash)) == NULL) { + hash.tag = strdup(tag); + hash.tracetype = type; + hash.id = 0; /* the ring buffer is never used to resolve indoms */ + hash.padding = 0; + hash.realcount = 1; + hash.realtime = data; + hash.taglength = (unsigned int)taglen; + hash.fd = clientfd; + hash.txcount = 1; + hash.txmin = hash.txmax = data; + hash.txsum = data; + hptr = &hash; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "fresh interval data on fd=%d rpos=%d " + "('%s': len=%d type=%d value=%d)", clientfd, rpos, + hash.tag, taglen, type, (int)data); +#endif + if (__pmhashinsert(ringbuf.ring[rpos].stats, hash.tag, hptr) < 0) { + __pmNotifyErr(LOG_ERR, "ring buffer insert failure - '%s' " + "data ignored", hash.tag); + free(hash.tag); + return -1; + } + } + else { /* update existing entry */ + hptr->txcount++; + if (hptr->tracetype == TRACE_TYPE_TRANSACT) { + if (data < hptr->txmin) + hptr->txmin = data; + if (data > hptr->txmax) + hptr->txmax = data; + hptr->txsum += data; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Updating data on fd=%d ('%s': type=%d " + "count=%d min=%f max=%f sum=%f)", + clientfd, hptr->tag, hptr->tracetype, + hptr->txcount, hptr->txmin, hptr->txmax, hptr->txsum); +#endif + } + if (freeflag) { + free(newhash.tag); + newhash.tag = NULL; + } + + return hptr->tracetype; +} + +static void +clearTable(hashtable_t *t, void *entry) +{ + hashdata_t *h = (hashdata_t *)entry; + h->txcount = -1; /* flag as out-of-date */ +} + +/* + * Goes off at set time interval. The old `tail' becomes the new `head' + * of the ring buffer & is marked as currently in-progess (and this data + * is not exported until the next timer event). + */ +void +timerUpdate(void) +{ + /* summary table must be reset for next fetch */ + if (dosummary == 0) { + __pmhashtraverse(&summary, clearTable); + dosummary = 1; + } + + if (ringbuf.ring[rpos].working == 0) { + __pmNotifyErr(LOG_ERR, "buffered I/O error - ignoring timer event"); + return; + } + + ringbuf.ring[rpos].working = 0; + if (rpos == rbufsize-1) + rpos = 0; /* return to start of buffer */ + else + rpos++; + __pmhashtrunc(ringbuf.ring[rpos].stats); /* new working set */ + ringbuf.ring[rpos].working = 1; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Alarm - working buffer is now %d/%d", + rpos+1, rbufsize); +#endif +} + +static void +summariseDataAux(hashtable_t *t, void *entry) +{ + hashdata_t *hptr; + hashdata_t *base = (hashdata_t *)entry; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "summariseDataAux: looking up '%s'", base->tag); +#endif + /* update summary hash table */ + if ((hptr = __pmhashlookup(&summary, base->tag, base)) == NULL) + __pmNotifyErr(LOG_ERR, "summariseDataAux: entry in ring buffer, " + "but not summary ('%s')", base->tag); + else { /* update an existing summary */ + if (hptr->tracetype == TRACE_TYPE_TRANSACT) { + if (hptr->txcount == -1) { /* reset coz flagged as out-of-date */ + tindomsize++; + hptr->txcount = base->txcount; + hptr->txmin = base->txmin; + hptr->txmax = base->txmax; + hptr->txsum = base->txsum; + } + else { + hptr->txcount += base->txcount; + if (base->txmin < hptr->txmin) + hptr->txmin = base->txmin; + if (base->txmax > hptr->txmax) + hptr->txmax = base->txmax; + hptr->txsum += base->txsum; + } + } + else { + if (hptr->txcount == -1) { /* reset coz flagged as out-of-date */ + if (hptr->tracetype == TRACE_TYPE_POINT) + pindomsize++; + else if (hptr->tracetype == TRACE_TYPE_COUNTER) + cindomsize++; + else if (hptr->tracetype == TRACE_TYPE_OBSERVE) + oindomsize++; + else { + __pmNotifyErr(LOG_ERR, + "bogus trace type - skipping '%s'", hptr->tag); + return; + } + hptr->txcount = base->txcount; + } + else { + hptr->txcount += base->txcount; + } + } + } +} + + +/* + * Create the summary hash table, as well as the instance list for + * this set of intervals. + */ +static void +summariseData(void) +{ + int count; + +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "summariseData: resummarising"); +#endif + /* initialise counters */ + tindomsize = pindomsize = oindomsize = 0; + + /* create the new summary table */ + for (count=0; count < rbufsize; count++) { + if (ringbuf.ring[count].working == 1) + continue; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "ring buffer table %d/%d has %d entries.\n", + count, rbufsize, ringbuf.ring[count].stats->entries); +#endif + __pmhashtraverse(ringbuf.ring[count].stats, summariseDataAux); + } + + /* summary now holds correct data for the rest of this interval */ + dosummary = 0; +} + +/* + * Try to keep trace fetching similar to libpcp_pmda pmdaFetch() + * we need a different way to get at the instances though + * so, we can still use __pmdaStartInst, but trace iterator + * is a bit different. + */ +static pmdaInstid * +nextTraceInst(pmdaExt *pmda) +{ + static pmdaInstid in = { PM_INDOM_NULL, NULL }; + pmdaInstid *iptr = NULL; + + if (pmda->e_singular == 0) { + /* PM_INDOM_NULL ... just the one value */ + iptr = ∈ + pmda->e_singular = -1; + } + if (pmda->e_ordinal >= 0) { + int j; + for (j = pmda->e_ordinal; j < pmda->e_idp->it_numinst; j++) { + if (__pmInProfile(pmda->e_idp->it_indom, pmda->e_prof, + pmda->e_idp->it_set[j].i_inst)) { + iptr = &pmda->e_idp->it_set[j]; + pmda->e_ordinal = j+1; + break; + } + } + } + return iptr; +} + +static int +auxFetch(int inst, __pmID_int *idp, char *tag, pmAtomValue *atom) +{ + if (inst != PM_IN_NULL && idp->cluster != 0) + return PM_ERR_INST; + + /* transaction, point, counter and observe trace values and control data */ + if (idp->cluster == 0) { + hashdata_t hash; + hashdata_t *hptr; + + hash.tag = tag; + + switch (idp->item) { + case 0: /* trace.transact.count */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->ull = hptr->realcount; + break; + case 1: /* trace.transact.rate */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)((double)hptr->txcount/(double)timespan.tv_sec); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "rate=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txcount, (double)timespan.tv_sec, tag); +#endif + break; + case 2: /* trace.transact.ave_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + else if (hptr->txcount == 0) + atom->f = 0; + else { + atom->f = (float)((double)hptr->txsum/(double)hptr->txcount); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "ave_time=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txsum, (double)hptr->txcount, tag); +#endif + } + break; + case 3: /* trace.transact.min_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)hptr->txmin; + break; + case 4: /* trace.transact.max_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)hptr->txmax; + break; + case 5: /* trace.transact.total_time */ + hash.tracetype = TRACE_TYPE_TRANSACT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->d = hptr->realtime; + break; + case 6: /* trace.point.count */ + hash.tracetype = TRACE_TYPE_POINT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->ull = hptr->realcount; + break; + case 7: /* trace.point.rate */ + hash.tracetype = TRACE_TYPE_POINT; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)((double)hptr->txcount/(double)timespan.tv_sec); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "rate=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txcount, (double)timespan.tv_sec, tag); +#endif + break; + case 8: /* trace.observe.count */ + case 17: /* trace.counter.count */ + hash.tracetype = (idp->item == 8)? + TRACE_TYPE_OBSERVE : TRACE_TYPE_COUNTER; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->ull = hptr->realcount; + break; + case 9: /* trace.observe.rate */ + case 18: /* trace.counter.rate */ + hash.tracetype = (idp->item == 9)? + TRACE_TYPE_OBSERVE : TRACE_TYPE_COUNTER; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + if (hptr->txcount < 0) /* not in current time period */ + return 0; + atom->f = (float)((double)hptr->txcount/(double)timespan.tv_sec); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL1) + __pmNotifyErr(LOG_DEBUG, "rate=%f=%f/%f('%s')\n", (float)atom->f, + (double)hptr->txcount, (double)timespan.tv_sec, tag); +#endif + break; + case 10: /* trace.observe.value */ + case 19: /* trace.counter.value */ + hash.tracetype = (idp->item == 10)? + TRACE_TYPE_OBSERVE : TRACE_TYPE_COUNTER; + if ((hptr = __pmhashlookup(&summary, hash.tag, &hash)) == NULL) + return PM_ERR_INST; + atom->d = hptr->txsum; + break; + case 11: /* trace.control.timespan */ + atom->ul = timespan.tv_sec; + break; + case 12: /* trace.control.interval */ + atom->ul = interval.tv_sec; + break; + case 13: /* trace.control.buckets */ + atom->ul = rbufsize-1; + break; + case 14: /* trace.control.port */ + atom->ul = ctlport; + break; + case 15: /* trace.control.reset */ + atom->ul = 1; + break; + case 16: /* trace.control.debug */ + atom->ul = pmDebug; + break; + default: + return PM_ERR_PMID; + } + } + else + return PM_ERR_PMID; + + return 1; +} + +static int +getIndomSize(__pmID_int *pmidp) +{ + int size; + + if (pmidp->cluster != 0) + return 1; + + switch (pmidp->item) { + case 0: /* uses summary's real counters (transact) */ + case 5: + size = indomtab[TRANSACT_INDOM].it_numinst; + break; + case 1: + case 2: + case 3: + case 4: /* susceptible to ring buffer updates */ + size = tindomsize; + break; + case 6: /* uses summary's real counters (point) */ + size = indomtab[POINT_INDOM].it_numinst; + break; + case 7: /* susceptible to ring buffer updates */ + size = pindomsize; + break; + case 8: + case 10: /* uses summary's real counters & data (obs) */ + size = indomtab[OBSERVE_INDOM].it_numinst; + break; + case 9: /* susceptible to ring buffer updates */ + size = oindomsize; + break; + case 17: + case 19: /* uses summary's real counters & data (ctr) */ + size = indomtab[COUNTER_INDOM].it_numinst; + break; + case 18: /* susceptible to ring buffer updates */ + size = cindomsize; + break; + default: + size = 0; + } + return size; +} + + +static int +traceFetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + static int maxnpmids = 0; + static pmResult *res = NULL; + pmValueSet *vset; + pmDesc *dp; + __pmID_int *pmidp; + pmdaMetric *metap; + pmAtomValue atom; + pmdaInstid *ins; + pmdaInstid noinst = { PM_IN_NULL, NULL }; + int numval; + int sts, i, j, need; + + indomSortCheck(); + pmda->e_idp = indomtab; + + if (numpmid > maxnpmids) { + if (res != NULL) + free(res); + /* (numpmid - 1) because there's room for one valueSet in a pmResult */ + need = (int)sizeof(pmResult) + (numpmid-1)*(int)sizeof(pmValueSet *); + if ((res = (pmResult *) malloc(need)) == NULL) { + return -oserror(); + } + maxnpmids = numpmid; + } + + res->timestamp.tv_sec = 0; + res->timestamp.tv_usec = 0; + res->numpmid = numpmid; + + for (i = 0; i < numpmid; i++) { + dp = NULL; + metap = NULL; + pmidp = (__pmID_int *)&pmidlist[i]; + if (pmda->e_direct) { + if (pmidp->item < pmda->e_nmetrics && + pmidlist[i] == pmda->e_metrics[pmidp->item].m_desc.pmid) { + metap = &pmda->e_metrics[pmidp->item]; + dp = &(metap->m_desc); + } + } + else { /* search for it */ + for (j = 0; j < pmda->e_nmetrics; j++) { + if (pmidlist[i] == pmda->e_metrics[j].m_desc.pmid) { + metap = &pmda->e_metrics[j]; + dp = &(metap->m_desc); + break; + } + } + } + if (dp == NULL) { + __pmNotifyErr(LOG_ERR, "traceFetch: Requested metric %s is not " + "defined", pmIDStr(pmidlist[i])); + numval = PM_ERR_PMID; + } + else if (dp->indom != PM_INDOM_NULL) { + /* + * Only summarise when you have to, so check if data has + * already been summarised within this interval. + */ + if (dosummary == 1) + summariseData(); + numval = getIndomSize(pmidp); +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "instance domain for %s numval=%d", + pmIDStr(dp->pmid), numval); +#endif + } + else + numval = 1; + + /* Must use individual malloc()s because of pmFreeResult() */ + if (numval >= 1) + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)+ + (numval - 1)*sizeof(pmValue)); + else + res->vset[i] = vset = (pmValueSet *)malloc(sizeof(pmValueSet)- + sizeof(pmValue)); + if (vset == NULL) { + if ((res->numpmid = i) > 0) + __pmFreeResultValues(res); + return -oserror(); + } + + vset->pmid = pmidlist[i]; + vset->numval = numval; + vset->valfmt = PM_VAL_INSITU; + if (vset->numval <= 0) + continue; + + if (dp->indom == PM_INDOM_NULL) + ins = &noinst; + else { + __pmdaStartInst(dp->indom, pmda); + ins = nextTraceInst(pmda); + } + j = 0; + do { + if (ins == NULL) { + __pmNotifyErr(LOG_ERR, "bogus instance ignored (pmid=%s)", + pmIDStr(dp->pmid)); + if ((res->numpmid = i) > 0) + __pmFreeResultValues(res); + return PM_ERR_INST; + } + if (j == numval) { + numval++; + res->vset[i] = vset = (pmValueSet *)realloc(vset, + sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue)); + if (vset == NULL) { + if ((res->numpmid = i) > 0) + __pmFreeResultValues(res); + return -oserror(); + } + } + vset->vlist[j].inst = ins->i_inst; + if ((sts = auxFetch(ins->i_inst, pmidp, ins->i_name, &atom)) < 0) { + if (sts == PM_ERR_PMID) + __pmNotifyErr(LOG_ERR, "unknown PMID requested - '%s'", + pmIDStr(dp->pmid)); + else if (sts == PM_ERR_INST) + __pmNotifyErr(LOG_ERR, "unknown instance requested - %d " + "(pmid=%s)", ins->i_inst, pmIDStr(dp->pmid)); + else + __pmNotifyErr(LOG_ERR, "fetch error (pmid=%s): %s", + pmIDStr(dp->pmid), pmErrStr(sts)); + } + else if (sts == 0) { /* not current, so don't use */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Instance is dated %s (pmid=%s)", + ins->i_name, pmIDStr(dp->pmid)); +#endif + } + else if ((sts = __pmStuffValue(&atom, &vset->vlist[j], dp->type)) == PM_ERR_TYPE) + __pmNotifyErr(LOG_ERR, "bad desc type (%d) for metric %s", + dp->type, pmIDStr(dp->pmid)); + else if (sts >= 0) { + vset->valfmt = sts; + j++; +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "Instance is good! %s (pmid=%s)", + ins->i_name, pmIDStr(dp->pmid)); +#endif + } + } while (dp->indom != PM_INDOM_NULL && + (ins = nextTraceInst(pmda)) != NULL); + if (j == 0) + vset->numval = sts; + else + vset->numval = j; + } + *resp = res; + + return 0; +} + + +static int +traceStore(pmResult *result, pmdaExt *pmda) +{ + int i, j; + int sts = 0; + pmValueSet *vsp = NULL; + __pmID_int *pmidp = NULL; + pmAtomValue av; + extern int afid; + extern void alarming(int, void *); + + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster != 0) + return PM_ERR_PMID; + + if (pmidp->item == 15) { /* trace.control.reset */ +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "resetting trace metrics"); +#endif + /* reset the interval timer */ + if (afid >= 0) { + __pmAFunregister(afid); + if ((afid = __pmAFregister(&interval, NULL, alarming)) < 0) { + __pmNotifyErr(LOG_ERR, "__pmAFregister failed"); + exit(1); + } + } + + /* reset summary and ring buffer hash tables */ + __pmhashtrunc(&summary); + for (j = 0; j < rbufsize; j++) { + __pmhashtrunc(ringbuf.ring[j].stats); + ringbuf.ring[j].numstats = 0; + } + + /* clear all the instance domain entries */ + if (indomtab[TRANSACT_INDOM].it_set) { + free(indomtab[TRANSACT_INDOM].it_set); + indomtab[TRANSACT_INDOM].it_set = NULL; + } + if (indomtab[OBSERVE_INDOM].it_set) { + free(indomtab[OBSERVE_INDOM].it_set); + indomtab[OBSERVE_INDOM].it_set = NULL; + } + if (indomtab[COUNTER_INDOM].it_set) { + free(indomtab[COUNTER_INDOM].it_set); + indomtab[COUNTER_INDOM].it_set = NULL; + } + if (indomtab[POINT_INDOM].it_set) { + free(indomtab[POINT_INDOM].it_set); + indomtab[POINT_INDOM].it_set = NULL; + } + indomtab[TRANSACT_INDOM].it_numinst = 0; + indomtab[COUNTER_INDOM].it_numinst = 0; + indomtab[OBSERVE_INDOM].it_numinst = 0; + indomtab[POINT_INDOM].it_numinst = 0; + tindomsize = pindomsize = oindomsize = 0; + /* definately need to recompute the summary next fetch */ + dosummary = 1; + __pmNotifyErr(LOG_INFO, "PMDA reset"); + } + else if (pmidp->item == 16) { /* trace.control.debug */ + if (vsp->numval != 1 || vsp->valfmt != PM_VAL_INSITU) + sts = PM_ERR_CONV; + else if (sts >= 0 && ((sts = pmExtractValue(vsp->valfmt, + &vsp->vlist[0], PM_TYPE_32, &av, PM_TYPE_32)) >= 0)) { + if (pmDebug != av.l) { + pmDebug = av.l; + __pmNotifyErr(LOG_INFO, "debug level set to %d", pmDebug); + debuglibrary(pmDebug); + } + } + } + else + sts = PM_ERR_PMID; + } + return sts; +} + + +/* + * Initialise the agent + */ +void +traceInit(pmdaInterface *dp) +{ + int rsize, sts; + + if (dp->status != 0) + return; + + dp->version.two.fetch = traceFetch; + dp->version.two.store = traceStore; + dp->version.two.instance = traceInstance; + dp->version.two.ext->e_direct = 0; + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); + + /* initialise ring buffer */ + rsize = (int)(sizeof(statlist_t) * rbufsize); + if ((ringbuf.ring = (statlist_t *)malloc(rsize)) == NULL) { + __pmNotifyErr(LOG_ERR, "failed during ring buffer initialise: %s", + osstrerror()); + exit(1); + } + for (rsize=0; rsize < rbufsize; rsize++) { + if ((ringbuf.ring[rsize].stats = (hashtable_t *) + malloc(sizeof(hashtable_t))) == NULL) { + __pmNotifyErr(LOG_ERR, "ring buffer initialise failed: %s", + osstrerror()); + exit(1); + } + if ((sts = __pmhashinit(ringbuf.ring[rsize].stats, 0, sizeof(hashdata_t), + datacmp, datadel)) < 0) { + __pmNotifyErr(LOG_ERR, "ring buffer initialisation failed: %s", + osstrerror()); + exit(1); + } + ringbuf.ring[rsize].working = 0; + } + rpos = 0; + ringbuf.ring[rpos].working = 1; + + /* initialise summary & associated instance domain */ + indomtab[TRANSACT_INDOM].it_numinst = 0; + indomtab[TRANSACT_INDOM].it_set = NULL; + indomtab[POINT_INDOM].it_numinst = 0; + indomtab[POINT_INDOM].it_set = NULL; + dp->version.two.ext->e_idp = indomtab; + if ((sts = __pmhashinit(&summary, 0, sizeof(hashdata_t), + datacmp, summarydel)) < 0) { + __pmNotifyErr(LOG_ERR, "summary table initialisation failed: %s", + osstrerror()); + exit(1); + } + /* initialise list of reserved instance domains (for store recovery) */ + if ((sts = __pmhashinit(&history, 0, sizeof(instdata_t), + instcmp, instdel)) < 0) { + __pmNotifyErr(LOG_ERR, "history table initialisation failed: %s", + osstrerror()); + exit(1); + } +} diff --git a/src/pmdas/trace/stub.c b/src/pmdas/trace/stub.c new file mode 100644 index 0000000..08790be --- /dev/null +++ b/src/pmdas/trace/stub.c @@ -0,0 +1,136 @@ +/* + * stub.c - libpcp_trace stubs + * + * 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pcp/trace.h> + +int __pmstate = 0; + +int +pmtracebegin(const char *tag) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtracebegin: start of transaction '%s'\n", tag); +#endif + return 0; +} + +int +pmtraceend(const char *tag) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtraceend: end of transaction '%s'\n", tag); +#endif + return 0; +} + +int +pmtraceabort(const char *tag) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtraceabort: transaction '%s' aborted\n", tag); +#endif + return 0; +} + +int +pmtraceobs(const char *label, double value) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtraceobs: observation '%s', value=%f\n", label, value); +#endif + return 0; +} + +int +pmtracecounter(const char *label, double value) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtracecounter: counter '%s', value=%f\n", label, value); +#endif + return 0; +} + +int +pmtracepoint(const char *label) +{ +#ifdef PMTRACE_DEBUG + if (__pmstate & PMTRACE_STATE_API) + fprintf(stderr, "pmtracepoint: trace point '%s' reached\n", label); +#endif + return 0; +} + +int +pmtracestate(int code) +{ + return(__pmstate |= code); +} + +char * +pmtraceerrstr(int code) +{ + static const struct { + int code; + char *msg; + } errtab[] = { + { PMTRACE_ERR_TAGNAME, + "Invalid tag name - tag names cannot be NULL" }, + { PMTRACE_ERR_INPROGRESS, + "Transaction is already in progress - cannot begin" }, + { PMTRACE_ERR_NOPROGRESS, + "Transaction is not currently in progress - cannot end" }, + { PMTRACE_ERR_NOSUCHTAG, + "Transaction tag was not successfully initialised" }, + { PMTRACE_ERR_TAGTYPE, + "Tag is already in use for a different type of tracing" }, + { PMTRACE_ERR_TAGLENGTH, + "Tag name is too long (maximum 256 characters)" }, + { PMTRACE_ERR_IPC, + "IPC protocol failure" }, + { PMTRACE_ERR_ENVFORMAT, + "Unrecognised environment variable format" }, + { PMTRACE_ERR_TIMEOUT, + "Application timed out connecting to the PMDA" }, + { PMTRACE_ERR_VERSION, + "Incompatible versions between application and PMDA" }, + { PMTRACE_ERR_PERMISSION, + "Cannot connect to PMDA - permission denied" }, + { PMTRACE_ERR_CONNLIMIT, + "Cannot connect to PMDA - connection limit reached" }, + { 0, "" } + }; + + if ((code < 0) && (code > -PMTRACE_ERR_BASE)) /* catch intro(2) errors */ + return strerror(-code); + else if (code == 0) + return "No error"; + else { + int i; + for (i=0; errtab[i].code; i++) { + if (errtab[i].code == code) + return errtab[i].msg; + } + } + return "Unknown error code"; +} |