summaryrefslogtreecommitdiff
path: root/src/pmdas/trace
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/trace')
-rw-r--r--src/pmdas/trace/GNUmakefile109
-rw-r--r--src/pmdas/trace/GNUmakefile.stub69
-rw-r--r--src/pmdas/trace/Install275
-rw-r--r--src/pmdas/trace/Makefile.proto69
-rw-r--r--src/pmdas/trace/README62
-rw-r--r--src/pmdas/trace/README.demos71
-rw-r--r--src/pmdas/trace/Remove38
-rw-r--r--src/pmdas/trace/app1.c97
-rw-r--r--src/pmdas/trace/app2.c163
-rw-r--r--src/pmdas/trace/app3.c166
-rw-r--r--src/pmdas/trace/fapp1.f102
-rw-r--r--src/pmdas/trace/help156
-rw-r--r--src/pmdas/trace/japp1.java46
-rw-r--r--src/pmdas/trace/pmns62
-rw-r--r--src/pmdas/trace/root10
-rw-r--r--src/pmdas/trace/src/GNUmakefile57
-rw-r--r--src/pmdas/trace/src/client.c155
-rw-r--r--src/pmdas/trace/src/client.h38
-rw-r--r--src/pmdas/trace/src/comms.c289
-rw-r--r--src/pmdas/trace/src/comms.h26
-rw-r--r--src/pmdas/trace/src/data.c122
-rw-r--r--src/pmdas/trace/src/data.h68
-rw-r--r--src/pmdas/trace/src/pmda.c228
-rw-r--r--src/pmdas/trace/src/trace.c1151
-rw-r--r--src/pmdas/trace/stub.c136
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, &timespan, &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 = &in;
+ 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";
+}