diff options
Diffstat (limited to 'src/pmdas/mmv')
-rw-r--r-- | src/pmdas/mmv/GNUmakefile | 62 | ||||
-rw-r--r-- | src/pmdas/mmv/Makefile.demos | 33 | ||||
-rw-r--r-- | src/pmdas/mmv/README.demos | 26 | ||||
-rw-r--r-- | src/pmdas/mmv/acme.c | 125 | ||||
-rw-r--r-- | src/pmdas/mmv/mmvdump.c | 346 | ||||
-rw-r--r-- | src/pmdas/mmv/src/GNUmakefile | 59 | ||||
-rwxr-xr-x | src/pmdas/mmv/src/Install | 36 | ||||
-rwxr-xr-x | src/pmdas/mmv/src/Remove | 23 | ||||
-rw-r--r-- | src/pmdas/mmv/src/mmv.c | 915 | ||||
-rw-r--r-- | src/pmdas/mmv/src/root_mmv | 13 |
10 files changed, 1638 insertions, 0 deletions
diff --git a/src/pmdas/mmv/GNUmakefile b/src/pmdas/mmv/GNUmakefile new file mode 100644 index 0000000..e674bd1 --- /dev/null +++ b/src/pmdas/mmv/GNUmakefile @@ -0,0 +1,62 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2009-2010 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = mmv +TARGET = mmvdump$(EXECSUFFIX) +CFILES = mmvdump.c + +SUBDIRS = src + +LCFILES = acme.c +LTARGET = acme$(EXECSUFFIX) +LSRCFILES = $(LCFILES) README.demos Makefile.demos +TARGETS = $(TARGET) $(LTARGET) + +LLDFLAGS = -L$(TOPDIR)/src/libpcp_mmv/src -L$(TOPDIR)/src/libpcp/src +LLDLIBS = -lpcp_mmv $(PCPLIB) +LDIRT = mmvdump acme + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) +DEMODIR = $(PCP_DEMOS_DIR)/$(IAM) +DEMOFILES = README.demos Makefile.demos +CONF_LINE = "mmv 70 dso mmv_init $(PCP_PMDAS_DIR)/mmv/pmda_mmv.$(DSOSUFFIX)" + +default_pcp default :: $(TARGETS) + +default_pcp default :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + @if [ `grep -c $(CONF_LINE) ../pmcd.conf` -eq 0 ]; then \ + echo $(CONF_LINE) >> ../pmcd.conf ; \ + fi + +include $(BUILDRULES) + +install_pcp install :: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +install_pcp install :: $(SUBDIRS) + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 $(TARGET) $(PMDADIR)/$(TARGET) + $(INSTALL) -m 755 -d $(DEMODIR) + $(INSTALL) -m 644 Makefile.demos $(DEMODIR)/Makefile + $(INSTALL) -m 644 README.demos $(DEMODIR)/README + $(INSTALL) -m 644 $(CFILES) $(LCFILES) $(DEMODIR) + +# check-build only, binary not installed (but source is) +$(LTARGET): acme.c + $(CCF) -o $@ $^ $(LDFLAGS) $(LDLIBS) diff --git a/src/pmdas/mmv/Makefile.demos b/src/pmdas/mmv/Makefile.demos new file mode 100644 index 0000000..5c6341c --- /dev/null +++ b/src/pmdas/mmv/Makefile.demos @@ -0,0 +1,33 @@ +# +# Copyright (c) 2013 Red Hat. +# +# 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 +CFLAGS = -g + +TARGETS = acme mmvdump + +default: $(TARGETS) + +mmvdump: mmvdump.c + $(CC) $(CFLAGS) -o $@ $^ -lpcp + +acme: acme.c + $(CC) $(CFLAGS) -o $@ $^ -lpcp_mmv -lpcp + +clean: + rm -f *.o + +clobber: clean + rm -f $(TARGETS) diff --git a/src/pmdas/mmv/README.demos b/src/pmdas/mmv/README.demos new file mode 100644 index 0000000..5ed4ef9 --- /dev/null +++ b/src/pmdas/mmv/README.demos @@ -0,0 +1,26 @@ +sample pcp_mmv applications +=========================== + +acme.c + is a sample application that is instrumented with the pcp_mmv +interface to generate metrics available from the MMV PMDA +(Performance Metrics Domain Agent). It runs without exiting, +and continually updates the mmv.acme.* metric values. Detailed +discussion about the workings of this application and each of +the instrumentation API calls it uses, is available as part of +the "Performance Co-Pilot Programmer's Guide". + +mmvdump.c + an example program that dumps the contents of a (memory mapped) +mmv(5) format file, as created by the pcp_mmv library and read by +the MMV PMDA. The binary is also shipped as part of pcp and can +be found installed below ${PCP_PMDAS_DIR}/mmv. + + +All source is shipped as part of pcp as well and is installed in +${PCP_DEMOS_DIR}/mmv. If you have a C toolchain installed, the +sources and Makefile in this directory may be used to create the +functionally equivalent binaries, by entering the command + + % make acme mmvdump + diff --git a/src/pmdas/mmv/acme.c b/src/pmdas/mmv/acme.c new file mode 100644 index 0000000..212ce39 --- /dev/null +++ b/src/pmdas/mmv/acme.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2013 Red Hat. + * + * 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 <pcp/pmapi.h> +#include <pcp/mmv_stats.h> + +static mmv_instances_t products[] = { + { .internal = 0, .external = "Anvils" }, + { .internal = 1, .external = "Rockets" }, + { .internal = 2, .external = "Giant_Rubber_Bands" }, +}; +#define ACME_PRODUCTS_INDOM 61 +#define ACME_PRODUCTS_COUNT (sizeof(products)/sizeof(products[0])) + +static mmv_indom_t indoms[] = { + { .serial = ACME_PRODUCTS_INDOM, + .count = ACME_PRODUCTS_COUNT, + .instances = products, + .shorttext = "Acme products", + .helptext = "Most popular products produced by the Acme Corporation", + }, +}; + +static mmv_metric_t metrics[] = { + { .name = "products.count", + .item = 7, + .type = MMV_TYPE_U64, + .semantics = MMV_SEM_COUNTER, + .dimension = MMV_UNITS(0,0,1,0,0,PM_COUNT_ONE), + .indom = ACME_PRODUCTS_INDOM, + .shorttext = "Acme factory product throughput", + .helptext = +"Monotonic increasing counter of products produced in the Acme Corporation\n" +"factory since starting the Acme production application. Quality guaranteed.", + }, + { .name = "products.time", + .item = 8, + .type = MMV_TYPE_U64, + .semantics = MMV_SEM_COUNTER, + .dimension = MMV_UNITS(0,1,0,0,PM_TIME_USEC,0), + .indom = ACME_PRODUCTS_INDOM, + .shorttext = "Machine time spent producing Acme products", + .helptext = +"Machine time spent producing Acme Corporation products. Does not include\n" +"time in queues waiting for production machinery.", + }, + { .name = "products.queuetime", + .item = 10, + .type = MMV_TYPE_U64, + .semantics = MMV_SEM_COUNTER, + .dimension = MMV_UNITS(0,1,0,0,PM_TIME_USEC,0), + .indom = ACME_PRODUCTS_INDOM, + .shorttext = "Queued time while producing Acme products", + .helptext = +"Time spent in the queue waiting to build Acme Corporation products,\n" +"while some other Acme product was being built instead of this one.", + }, +}; + +#define INDOM_COUNT (sizeof(indoms)/sizeof(indoms[0])) +#define METRIC_COUNT (sizeof(metrics)/sizeof(metrics[0])) +#define ACME_CLUSTER 321 /* PMID cluster identifier */ + +int +main(int argc, char * argv[]) +{ + void *base; + pmAtomValue *count[ACME_PRODUCTS_COUNT]; + pmAtomValue *machine[ACME_PRODUCTS_COUNT]; + pmAtomValue *inqueue[ACME_PRODUCTS_COUNT]; + unsigned int working; + unsigned int product; + unsigned int i; + + base = mmv_stats_init("acme", ACME_CLUSTER, 0, + metrics, METRIC_COUNT, indoms, INDOM_COUNT); + if (!base) { + perror("mmv_stats_init"); + return 1; + } + + for (i = 0; i < ACME_PRODUCTS_COUNT; i++) { + count[i] = mmv_lookup_value_desc(base, + "products.count", products[i].external); + machine[i] = mmv_lookup_value_desc(base, + "products.time", products[i].external); + inqueue[i] = mmv_lookup_value_desc(base, + "products.queuetime", products[i].external); + } + + while (1) { + /* choose a random number between 0-N -> product */ + product = rand() % ACME_PRODUCTS_COUNT; + + /* assign a time spent "working" on this product */ + working = rand() % 50000; + + /* pretend to "work" so process doesn't burn CPU */ + usleep(working); + + /* update the memory mapped values for this one: */ + /* one more product produced and work time spent */ + mmv_inc_value(base, machine[product], working); /* API */ + count[product]->ull += 1; /* or direct mmap update */ + + /* all other products are "queued" for this time */ + for (i = 0; i < ACME_PRODUCTS_COUNT; i++) + if (i != product) + mmv_inc_value(base, inqueue[i], working); + } + + mmv_stats_stop("acme", base); + return 0; +} diff --git a/src/pmdas/mmv/mmvdump.c b/src/pmdas/mmv/mmvdump.c new file mode 100644 index 0000000..0943618 --- /dev/null +++ b/src/pmdas/mmv/mmvdump.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2013 Red Hat. + * Copyright (C) 2009 Aconex. All Rights Reserved. + * Copyright (C) 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 <pcp/pmapi.h> +#include <pcp/mmv_stats.h> +#include <pcp/mmv_dev.h> +#include <pcp/impl.h> +#include <inttypes.h> +#include <sys/stat.h> + +void +dump_indoms(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_string_t * string; + mmv_disk_indom_t * indom = (mmv_disk_indom_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, indoms offset %" PRIu64 " (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + __uint64_t off = offset + i * sizeof(mmv_disk_indom_t); + printf(" [%u/%"PRIi64"] %d instances, starting at offset %"PRIi64"\n", + indom[i].serial, off, indom[i].count, indom[i].offset); + if (indom[i].shorttext) { + string = (mmv_disk_string_t *) + ((char *)addr + indom[i].shorttext); + printf(" shorttext=%s\n", string->payload); + } + else + printf(" (no shorttext)\n"); + if (indom[i].helptext) { + string = (mmv_disk_string_t *) + ((char *)addr + indom[i].helptext); + printf(" helptext=%s\n", string->payload); + } + else + printf(" (no helptext)\n"); + } +} + +void +dump_insts(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_instance_t * inst = (mmv_disk_instance_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, instances offset %"PRIi64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + mmv_disk_indom_t * indom = (mmv_disk_indom_t *) + ((char *)addr + inst[i].indom); + printf(" [%u/%"PRIi64"] instance = [%d or \"%s\"]\n", + indom->serial, + (offset + i * sizeof(mmv_disk_instance_t)), + inst[i].internal, inst[i].external); + } +} + +static char * +metrictype(int mtype) +{ + char *type; + + switch (mtype) { + case MMV_TYPE_I32: + type = "32-bit int"; + break; + case MMV_TYPE_U32: + type = "32-bit unsigned int"; + break; + case MMV_TYPE_I64: + type = "64-bit int"; + break; + case MMV_TYPE_U64: + type = "64-bit unsigned int"; + break; + case MMV_TYPE_FLOAT: + type = "float"; + break; + case MMV_TYPE_DOUBLE: + type = "double"; + break; + case MMV_TYPE_STRING: + type = "string"; + break; + case MMV_TYPE_ELAPSED: + type = "elapsed"; + break; + default: + type = "?"; + break; + } + return type; +} + +static char * +metricsem(int msem) +{ + char *sem; + + switch (msem) { + case PM_SEM_COUNTER: + sem = "counter"; + break; + case PM_SEM_INSTANT: + sem = "instant"; + break; + case PM_SEM_DISCRETE: + sem = "discrete"; + break; + default: + sem = "?"; + break; + } + return sem; +} + +void +dump_metrics(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_string_t * string; + mmv_disk_metric_t * m = (mmv_disk_metric_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: toc offset %ld, metrics offset %"PRIi64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + __uint64_t off = offset + i * sizeof(mmv_disk_metric_t); + printf(" [%u/%"PRIi64"] %s\n", m[i].item, off, m[i].name); + printf(" type=%s (0x%x), sem=%s (0x%x), pad=0x%x\n", + metrictype(m[i].type), m[i].type, + metricsem(m[i].semantics), m[i].semantics, + m[i].padding); + printf(" units=%s\n", pmUnitsStr(&m[i].dimension)); + if (m[i].indom != PM_INDOM_NULL && m[i].indom != 0) + printf(" indom=%d\n", m[i].indom); + else + printf(" (no indom)\n"); + if (m[i].shorttext) { + string = (mmv_disk_string_t *) + ((char *)addr + m[i].shorttext); + printf(" shorttext=%s\n", string->payload); + } + else + printf(" (no shorttext)\n"); + if (m[i].helptext) { + string = (mmv_disk_string_t *) + ((char *)addr + m[i].helptext); + printf(" helptext=%s\n", string->payload); + } + else + printf(" (no helptext)\n"); + } +} + +void +dump_values(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_value_t * vals = (mmv_disk_value_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, values offset %"PRIu64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + mmv_disk_string_t * string; + mmv_disk_metric_t * m = (mmv_disk_metric_t *) + ((char *)addr + vals[i].metric); + __uint64_t off = offset + i * sizeof(mmv_disk_value_t); + + printf(" [%u/%"PRIu64"] %s", m->item, off, m->name); + if (m->indom && m->indom != PM_IN_NULL) { + mmv_disk_instance_t *indom = (mmv_disk_instance_t *) + ((char *)addr + vals[i].instance); + printf("[%d or \"%s\"]", + indom->internal, indom->external); + } + + switch (m->type) { + case MMV_TYPE_I32: + printf(" = %d", vals[i].value.l); + break; + case MMV_TYPE_U32: + printf(" = %u", vals[i].value.ul); + break; + case MMV_TYPE_I64: + printf(" = %" PRIi64, vals[i].value.ll); + break; + case MMV_TYPE_U64: + printf(" = %" PRIu64, vals[i].value.ull); + break; + case MMV_TYPE_FLOAT: + printf(" = %f", vals[i].value.f); + break; + case MMV_TYPE_DOUBLE: + printf(" = %lf", vals[i].value.d); + break; + case MMV_TYPE_STRING: + string = (mmv_disk_string_t *)((char *)addr + vals[i].extra); + printf(" = \"%s\"", string->payload); + break; + case MMV_TYPE_ELAPSED: { + struct timeval tv; + __int64_t t; + + __pmtimevalNow(&tv); + t = vals[i].value.ll; + if (vals[i].extra < 0) + t += ((tv.tv_sec*1e6 + tv.tv_usec) + vals[i].extra); + printf(" = %"PRIi64" (value=%"PRIi64"/extra=%"PRIi64")", + t, vals[i].value.ll, vals[i].extra); + if (vals[i].extra > 0) + printf("Bad ELAPSED 'extra' value found!"); + break; + } + default: + printf("Unknown type %d", m->type); + } + putchar('\n'); + } +} + +void +dump_strings(void *addr, int idx, long base, __uint64_t offset, __int32_t count) +{ + int i; + mmv_disk_string_t * string = (mmv_disk_string_t *) + ((char *)addr + offset); + + printf("\nTOC[%d]: offset %ld, string offset %"PRIu64" (%d entries)\n", + idx, base, offset, count); + + for (i = 0; i < count; i++) { + printf(" [%u/%"PRIu64"] %s\n", + i+1, offset + i * sizeof(mmv_disk_string_t), + string[i].payload); + } +} + +int +dump(const char *file, void *addr) +{ + int i; + mmv_disk_header_t * hdr = (mmv_disk_header_t *) addr; + mmv_disk_toc_t * toc = (mmv_disk_toc_t *) + ((char *)addr + sizeof(mmv_disk_header_t)); + + if (strcmp(hdr->magic, "MMV")) { + printf("Bad magic: %c%c%c\n", + hdr->magic[0], hdr->magic[1], hdr->magic[2]); + return 1; + } + if (hdr->version != MMV_VERSION) { + printf("version %d not supported\n", hdr->version); + return 1; + } + + printf("MMV file = %s\n", file); + printf("Version = %d\n", hdr->version); + printf("Generated = %"PRIu64"\n", hdr->g1); + if (hdr->g1 != hdr->g2) { + printf("Generated2 = %"PRIu64"\n", hdr->g2); + printf("Mismatched generation numbers\n"); + return 1; + } + printf("TOC count = %u\n", hdr->tocs); + printf("Cluster = %u\n", hdr->cluster); + printf("Process = %d\n", hdr->process); + printf("Flags = 0x%x\n", hdr->flags); + + for (i = 0; i < hdr->tocs; i++) { + __uint64_t base = ((char *)&toc[i] - (char *)addr); + switch (toc[i].type) { + case MMV_TOC_INDOMS: + dump_indoms(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_INSTANCES: + dump_insts(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_VALUES: + dump_values(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_METRICS: + dump_metrics(addr, i, base, toc[i].offset, toc[i].count); + break; + case MMV_TOC_STRINGS: + dump_strings(addr, i, base, toc[i].offset, toc[i].count); + break; + default: + printf("Unrecognised TOC[%d] type: 0x%x\n", i, toc[i].type); + } + } + return 0; +} + +int +main(int argc, char * argv[]) +{ + int fd; + char file[MAXPATHLEN]; + + if (argc > 2) { + printf("USAGE: %s <filename>\n", argv[0]); + exit(1); + } + if (argc > 1) + strncpy(file, argv[1], MAXPATHLEN); + else + snprintf(file, MAXPATHLEN, "%s%cmmv%ctest", + pmGetConfig("PCP_TMP_DIR"), + __pmPathSeparator(), __pmPathSeparator()); + file[MAXPATHLEN-1] = '\0'; + + if ((fd = open(file, O_RDONLY)) < 0) + perror(file); + else { + struct stat s; + void * addr; + + if (fstat(fd, &s) < 0) + perror(file); + else if ((addr = __pmMemoryMap(fd, s.st_size, 0)) != NULL) + return dump(file, addr); + } + return 1; +} diff --git a/src/pmdas/mmv/src/GNUmakefile b/src/pmdas/mmv/src/GNUmakefile new file mode 100644 index 0000000..421c38b --- /dev/null +++ b/src/pmdas/mmv/src/GNUmakefile @@ -0,0 +1,59 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2009-2010 Aconex. All Rights Reserved. +# Copyright (c) 2000-2001,2009 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 = mmv +DOMAIN = MMV +CMDTARGET = pmda$(IAM)$(EXECSUFFIX) +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +PMDAINIT = $(IAM)_init +TARGETS = $(CMDTARGET) $(LIBTARGET) + +CFILES = mmv.c +VERSION_SCRIPT = exports +LSRCFILES = Install Remove root_mmv +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = $(INVISIBILITY) +LDIRT = domain.h *.log pmns $(VERSION_SCRIPT) + +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +default_pcp default: $(TARGETS) pmns + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 644 domain.h $(PMDADIR)/domain.h + $(INSTALL) -m 755 $(TARGETS) Install Remove $(PMDADIR) + $(INSTALL) -m 644 pmns $(PMDADIR)/root_mmv + $(INSTALL) -m 644 root_mmv $(PCP_VAR_DIR)/pmns/root_mmv + +$(CMDTARGET): $(OBJECTS) + +$(IAM).o : domain.h +$(LIBTARGET) : $(VERSION_SCRIPT) + +$(VERSION_SCRIPT): + $(VERSION_SCRIPT_MAKERULE) + +domain.h: ../../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +pmns : + $(LN_S) -f root_mmv pmns diff --git a/src/pmdas/mmv/src/Install b/src/pmdas/mmv/src/Install new file mode 100755 index 0000000..d8f148b --- /dev/null +++ b/src/pmdas/mmv/src/Install @@ -0,0 +1,36 @@ +#! /bin/sh +# +# Copyright (C) 1997,2009 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (C) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mmv +dso_opt=true +socket_opt=true +socket_inet_def=2081 +forced_restart=false +pmda_interface=4 +pmns_source=root_mmv + +if [ ! -e "$PCP_TMP_DIR/mmv" ] +then + echo "creating $PCP_TMP_DIR/mmv" + mkdir -p -m 1777 "$PCP_TMP_DIR/mmv" +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/mmv/src/Remove b/src/pmdas/mmv/src/Remove new file mode 100755 index 0000000..a463371 --- /dev/null +++ b/src/pmdas/mmv/src/Remove @@ -0,0 +1,23 @@ +#! /bin/sh +# +# Copyright (C) 1997,2009 Silicon Graphics, Inc., All Rights Reserved. +# Copyright (C) 2009 Aconex. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=mmv +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/mmv/src/mmv.c b/src/pmdas/mmv/src/mmv.c new file mode 100644 index 0000000..ecc97ed --- /dev/null +++ b/src/pmdas/mmv/src/mmv.c @@ -0,0 +1,915 @@ +/* + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 2009-2010 Aconex. All Rights Reserved. + * Copyright (c) 1995-2000,2009 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. + * + * + * MMV PMDA + * + * This PMDA uses specially formatted files in either /var/tmp/mmv or some + * other directory, as specified on the command line. Each file represents + * a separate "cluster" of values with flat name structure for each cluster. + * Names for the metrics are optionally prepended with mmv and then the name + * of the file (by default - this can be changed). + */ + +#include "pmapi.h" +#include "mmv_stats.h" +#include "mmv_dev.h" +#include "impl.h" +#include "pmda.h" +#include "./domain.h" +#include <sys/stat.h> +#include <ctype.h> + +static int isDSO = 1; +static char *username; + +/* command line option handling - both short and long options */ +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_END +}; +static pmdaOptions opts = { + .short_options = "D:d:l:U:?", + .long_options = longopts, +}; + +static pmdaMetric * metrics; +static int mcnt; +static pmdaIndom * indoms; +static int incnt; + +static int reload; +static __pmnsTree * pmns; +static int statsdir_code; /* last statsdir stat code */ +static time_t statsdir_ts; /* last statsdir timestamp */ +static char * prefix = "mmv"; + +static char * pcptmpdir; /* probably /var/tmp */ +static char * pcpvardir; /* probably /var/pcp */ +static char * pcppmdasdir; /* probably /var/pcp/pmdas */ +static char pmnsdir[MAXPATHLEN]; /* pcpvardir/pmns */ +static char statsdir[MAXPATHLEN]; /* pcptmpdir/<prefix> */ + +typedef struct { + char * name; /* strdup client name */ + void * addr; /* mmap */ + mmv_disk_value_t * values; /* values in mmap */ + mmv_disk_metric_t * metrics; /* metric descs in mmap */ + int vcnt; /* number of values */ + int mcnt; /* number of metrics */ + pid_t pid; /* process identifier */ + int cluster; /* cluster identifier */ + __int64_t len; /* mmap region len */ + __uint64_t gen; /* generation number on open */ +} stats_t; + +static stats_t * slist; +static int scnt; + +/* + * Choose an unused cluster ID while honouring specific requests. + * If a specific (non-zero) cluster is requested we always use it. + */ +static int +choose_cluster(int requested, const char *path) +{ + int i; + + if (!requested) { + int next_cluster = 1; + + for (i = 0; i < scnt; i++) { + if (slist[i].cluster == next_cluster) { + next_cluster++; + i = 0; /* restart, we're filling holes */ + } + } + return next_cluster; + } + + for (i = 0; i < scnt; i++) { + if (slist[i].cluster == requested) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, + "MMV: %s: duplicate cluster %d in use", + pmProgname, requested); + break; + } + } + return requested; +} + +static int +create_client_stat(const char *client, const char *path, size_t size) +{ + int fd; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: create_client_stat: %s, %s", client, path); + + if ((fd = open(path, O_RDONLY)) >= 0) { + void *m = __pmMemoryMap(fd, size, 0); + + close(fd); + if (m != NULL) { + mmv_disk_header_t * hdr = (mmv_disk_header_t *)m; + int cluster; + + if (strncmp(hdr->magic, "MMV", 4)) { + __pmMemoryUnmap(m, size); + return -EINVAL; + } + + if (hdr->version != MMV_VERSION) { + __pmNotifyErr(LOG_ERR, "%s: %s client version %d " + "not supported (current is %d)", + pmProgname, prefix, hdr->version, MMV_VERSION); + __pmMemoryUnmap(m, size); + return -ENOSYS; + } + + if (!hdr->g1 || hdr->g1 != hdr->g2) { + /* still in flux, wait till next time */ + __pmMemoryUnmap(m, size); + return -EAGAIN; + } + + /* optionally verify the creator PID is running */ + if (hdr->process && (hdr->flags & MMV_FLAG_PROCESS) && + !__pmProcessExists((pid_t)hdr->process)) { + __pmMemoryUnmap(m, size); + return -ESRCH; + } + + /* all checks out, we'll use this one */ + cluster = choose_cluster(hdr->cluster, path); + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: %s: loading %s client: %d \"%s\"", + pmProgname, prefix, cluster, path); + + slist = realloc(slist, sizeof(stats_t)*(scnt+1)); + if (slist != NULL) { + slist[scnt].name = strdup(client); + slist[scnt].addr = m; + slist[scnt].pid = (pid_t)((hdr->flags & MMV_FLAG_PROCESS)? hdr->process : 0); + slist[scnt].cluster = cluster; + slist[scnt].mcnt = 0; + slist[scnt].gen = hdr->g1; + slist[scnt].len = size; + scnt++; + } else { + __pmNotifyErr(LOG_ERR, "%s: client \"%s\" out of memory - %s", + pmProgname, client, osstrerror()); + __pmMemoryUnmap(m, size); + scnt = 0; + } + } else { + __pmNotifyErr(LOG_ERR, "%s: failed to memory map \"%s\" - %s", + pmProgname, path, osstrerror()); + } + } else { + __pmNotifyErr(LOG_ERR, "%s: failed to open client file \"%s\" - %s", + pmProgname, client, osstrerror()); + } + return 0; +} + +/* check validity of client metric name, return non-zero if bad or duplicate */ +static int +verify_metric_name(const char *name, int pos, stats_t *s) +{ + const char *p = name; + pmID pmid; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: verify_metric_name: %s", name); + + if (p == NULL || *p == '\0' || !isalpha((int)*p)) { + __pmNotifyErr(LOG_WARNING, "Invalid metric[%d] name start in %s, ignored", + pos, s->name); + return -EINVAL; + } + for (++p; (p != NULL && *p != '\0'); p++) { + if (isalnum((int)*p) || *p == '_' || *p == '.') + continue; + __pmNotifyErr(LOG_WARNING, "invalid metric[%d] name in %s (@%c), ignored", + pos, s->name, *p); + return -EINVAL; + } + if (pmdaTreePMID(pmns, name, &pmid) == 0) + return -EEXIST; + return 0; +} + +/* check client item number validity - must not be too large to fit in PMID! */ +static int +verify_metric_item(unsigned int item, char *name, stats_t *s) +{ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: verify_metric_item: %u - %s", item, name); + + if (pmid_item(item) != item) { + __pmNotifyErr(LOG_WARNING, "invalid item %u (%s) in %s, ignored", + item, name, s->name); + return -EINVAL; + } + return 0; +} + +static int +create_metric(pmdaExt *pmda, stats_t *s, mmv_disk_metric_t *m, char *name, pmID pmid) +{ + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: create_metric: %s - %s", name, pmIDStr(pmid)); + + metrics = realloc(metrics, sizeof(pmdaMetric) * (mcnt + 1)); + if (metrics == NULL) { + __pmNotifyErr(LOG_ERR, "cannot grow MMV metric list: %s", s->name); + return -ENOMEM; + } + + metrics[mcnt].m_user = NULL; + metrics[mcnt].m_desc.pmid = pmid; + + if (m->type == MMV_TYPE_ELAPSED) { + pmUnits unit = PMDA_PMUNITS(0,1,0,0,PM_TIME_USEC,0); + metrics[mcnt].m_desc.sem = PM_SEM_COUNTER; + metrics[mcnt].m_desc.type = MMV_TYPE_I64; + metrics[mcnt].m_desc.units = unit; + } else { + if (m->semantics) + metrics[mcnt].m_desc.sem = m->semantics; + else + metrics[mcnt].m_desc.sem = PM_SEM_COUNTER; + metrics[mcnt].m_desc.type = m->type; + memcpy(&metrics[mcnt].m_desc.units, &m->dimension, sizeof(pmUnits)); + } + metrics[mcnt].m_desc.indom = (!m->indom || m->indom == PM_INDOM_NULL) ? + PM_INDOM_NULL : pmInDom_build(pmda->e_domain, + (s->cluster << 11) | m->indom); + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: map_stats adding metric[%d] %s %s from %s\n", + mcnt, name, pmIDStr(pmid), s->name); + + mcnt++; + __pmAddPMNSNode(pmns, pmid, name); + + return 0; +} + +/* check client serial number validity, and check for a duplicate */ +static int +verify_indom_serial(pmdaExt *pmda, int serial, stats_t *s, pmInDom *p, pmdaIndom **i) +{ + int index; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: verify_indom_serial: %u", serial); + + if (pmInDom_serial(serial) != serial) { + __pmNotifyErr(LOG_WARNING, "invalid serial %u in %s, ignored", + serial, s->name); + return -EINVAL; + } + + *p = pmInDom_build(pmda->e_domain, (s->cluster << 11) | serial); + for (index = 0; index < incnt; index++) { + *i = &indoms[index]; + if (indoms[index].it_indom == *p) + return -EEXIST; + } + *i = NULL; + return 0; +} + +static int +update_indom(pmdaExt *pmda, stats_t *s, mmv_disk_indom_t *id, pmdaIndom *ip) +{ + int i, j, size, newinsts = 0; + mmv_disk_instance_t *in = (mmv_disk_instance_t *)((char *)s->addr + id->offset); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: update_indom: %u (%d insts)", + id->serial, ip->it_numinst); + + /* first calculate how many new instances, so we know what to alloc */ + for (i = 0; i < id->count; i++) { + for (j = 0; j < ip->it_numinst; j++) + if (ip->it_set[j].i_inst == in[i].internal) + continue; + if (j == ip->it_numinst) + newinsts++; + } + + if (!newinsts) + return 0; + + /* allocate memory, then append new instances to the known set */ + size = sizeof(pmdaInstid) * (ip->it_numinst + newinsts); + ip->it_set = (pmdaInstid *)realloc(ip->it_set, size); + if (ip->it_set != NULL) { + for (i = 0; i < id->count; i++) { + for (j = 0; j < ip->it_numinst; j++) + if (ip->it_set[j].i_inst == in[j].internal) + continue; + if (j == ip->it_numinst) { + ip->it_set[j].i_inst = in[i].internal; + ip->it_set[j].i_name = in[i].external; + ip->it_numinst++; + } + } + } else { + __pmNotifyErr(LOG_ERR, "%s: cannot get memory for instance list in %s", + pmProgname, s->name); + ip->it_numinst = 0; + return -ENOMEM; + } + return 0; +} + +static int +create_indom(pmdaExt *pmda, stats_t *s, mmv_disk_indom_t *id, pmInDom indom) +{ + int i; + pmdaIndom *ip; + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: create_indom: %u", id->serial); + + indoms = realloc(indoms, sizeof(pmdaIndom) * (incnt + 1)); + if (indoms == NULL) { + __pmNotifyErr(LOG_ERR, "%s: cannot grow indom list in %s", + pmProgname, s->name); + return -ENOMEM; + } + ip = &indoms[incnt++]; + ip->it_indom = indom; + ip->it_set = (pmdaInstid *)calloc(id->count, sizeof(pmdaInstid)); + if (ip->it_set != NULL) { + mmv_disk_instance_t * in = (mmv_disk_instance_t *) + ((char *)s->addr + id->offset); + ip->it_numinst = id->count; + for (i = 0; i < ip->it_numinst; i++) { + ip->it_set[i].i_inst = in[i].internal; + ip->it_set[i].i_name = in[i].external; + } + } else { + __pmNotifyErr(LOG_ERR, "%s: cannot get memory for instance list in %s", + pmProgname, s->name); + ip->it_numinst = 0; + return -ENOMEM; + } + return 0; +} + +static void +map_stats(pmdaExt *pmda) +{ + struct dirent **files; + char name[64]; + int need_reload = 0; + int i, j, k, sts, num; + + if (pmns) + __pmFreePMNS(pmns); + + if ((sts = __pmNewPMNS(&pmns)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create new pmns: %s\n", + pmProgname, pmErrStr(sts)); + pmns = NULL; + return; + } + + /* hard-coded metrics (not from mmap'd files */ + snprintf(name, sizeof(name), "%s.reload", prefix); + __pmAddPMNSNode(pmns, pmid_build(pmda->e_domain, 0, 0), name); + snprintf(name, sizeof(name), "%s.debug", prefix); + __pmAddPMNSNode(pmns, pmid_build(pmda->e_domain, 0, 1), name); + mcnt = 2; + + if (indoms != NULL) { + for (i = 0; i < incnt; i++) + free(indoms[i].it_set); + free(indoms); + indoms = NULL; + incnt = 0; + } + + if (slist != NULL) { + for (i = 0; i < scnt; i++) { + free(slist[i].name); + __pmMemoryUnmap(slist[i].addr, slist[i].len); + } + free(slist); + slist = NULL; + scnt = 0; + } + + num = scandir(statsdir, &files, NULL, NULL); + for (i = 0; i < num; i++) { + struct stat statbuf; + char path[MAXPATHLEN]; + char *client; + + if (files[i]->d_name[0] == '.') + continue; + + client = files[i]->d_name; + sprintf(path, "%s%c%s", statsdir, __pmPathSeparator(), client); + + if (stat(path, &statbuf) >= 0 && S_ISREG(statbuf.st_mode)) + if (create_client_stat(client, path, statbuf.st_size) == -EAGAIN) + need_reload = 1; + } + + for (i = 0; i < num; i++) + free(files[i]); + if (num > 0) + free(files); + + for (i = 0; slist && i < scnt; i++) { + stats_t * s = slist + i; + mmv_disk_header_t * hdr = (mmv_disk_header_t *)s->addr; + mmv_disk_toc_t * toc = (mmv_disk_toc_t *) + ((char *)s->addr + sizeof(mmv_disk_header_t)); + + for (j = 0; j < hdr->tocs; j++) { + switch (toc[j].type) { + case MMV_TOC_METRICS: { + mmv_disk_metric_t *ml = (mmv_disk_metric_t *) + ((char *)s->addr + toc[j].offset); + + s->metrics = ml; + s->mcnt = toc[j].count; + + for (k = 0; k < toc[j].count; k++) { + char name[MAXPATHLEN]; + pmID pmid; + + /* build name, check its legitimate and unique */ + if (hdr->flags & MMV_FLAG_NOPREFIX) + sprintf(name, "%s.", prefix); + else + sprintf(name, "%s.%s.", prefix, s->name); + strcat(name, ml[k].name); + if (verify_metric_name(name, k, s) != 0) + continue; + if (verify_metric_item(ml[k].item, name, s) != 0) + continue; + + pmid = pmid_build(pmda->e_domain, s->cluster, ml[k].item); + create_metric(pmda, s, &ml[k], name, pmid); + } + break; + } + + case MMV_TOC_INDOMS: { + mmv_disk_indom_t * id = (mmv_disk_indom_t *) + ((char *)s->addr + toc[j].offset); + + for (k = 0; k < toc[j].count; k++) { + int sts, serial = id[k].serial; + pmInDom pmindom; + pmdaIndom *ip; + + sts = verify_indom_serial(pmda, serial, s, &pmindom, &ip); + if (sts == -EINVAL) + continue; + else if (sts == -EEXIST) + /* see if we have new instances to add here */ + update_indom(pmda, s, &id[k], ip); + else + /* first time we've observed this indom */ + create_indom(pmda, s, &id[k], pmindom); + } + break; + } + + case MMV_TOC_VALUES: { + s->vcnt = toc[j].count; + s->values = (mmv_disk_value_t *) + ((char *)s->addr + toc[j].offset); + break; + } + + default: + break; + } + } + } + + pmdaTreeRebuildHash(pmns, mcnt); /* for reverse (pmid->name) lookups */ + reload = need_reload; +} + +static int +mmv_lookup_stat_metric_value(pmID pmid, unsigned int inst, + stats_t **sout, mmv_disk_metric_t **mout, mmv_disk_value_t **vout) +{ + __pmID_int * id = (__pmID_int *)&pmid; + mmv_disk_metric_t * m; + mmv_disk_value_t * v; + stats_t * s; + int si, mi, vi; + int sts = PM_ERR_PMID; + + for (si = 0; si < scnt; si++) { + s = &slist[si]; + if (s->cluster != id->cluster) + continue; + + m = s->metrics; + for (mi = 0; mi < s->mcnt; mi++) { + if (m[mi].item != id->item) + continue; + + sts = PM_ERR_INST; + v = s->values; + for (vi = 0; vi < s->vcnt; vi++) { + mmv_disk_metric_t * mt = (mmv_disk_metric_t *) + ((char *)s->addr + v[vi].metric); + mmv_disk_instance_t * is = (mmv_disk_instance_t *) + ((char *)s->addr + v[vi].instance); + + if ((mt == &m[mi]) && + (mt->indom == PM_INDOM_NULL || mt->indom == 0 || + inst == PM_IN_NULL || is->internal == inst)) { + *sout = s; + *mout = &m[mi]; + *vout = &v[vi]; + return 0; + } + } + } + } + return sts; +} + +/* + * callback provided to pmdaFetch + */ +static int +mmv_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int * id = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (id->cluster == 0) { + if (id->item == 0) { + atom->l = reload; + return 1; + } + if (id->item == 1) { + atom->l = pmDebug; + return 1; + } + return PM_ERR_PMID; + + } else if (scnt > 0) { /* We have at least one source of metrics */ + mmv_disk_string_t * str; + mmv_disk_metric_t * m; + mmv_disk_value_t * v; + stats_t * s; + int rv; + + rv = mmv_lookup_stat_metric_value(mdesc->m_desc.pmid, inst, &s, &m, &v); + if (rv < 0) + return rv; + + switch (m->type) { + case MMV_TYPE_I32: + case MMV_TYPE_U32: + case MMV_TYPE_I64: + case MMV_TYPE_U64: + case MMV_TYPE_FLOAT: + case MMV_TYPE_DOUBLE: + memcpy(atom, &v->value, sizeof(pmAtomValue)); + break; + case MMV_TYPE_ELAPSED: { + atom->ll = v->value.ll; + if (v->extra < 0) { /* inside a timed section */ + struct timeval tv; + __pmtimevalNow(&tv); + atom->ll += (tv.tv_sec * 1e6 + tv.tv_usec) + v->extra; + } + break; + } + case MMV_TYPE_STRING: { + str = (mmv_disk_string_t *)((char *)s->addr + v->extra); + atom->cp = str->payload; + break; + } + case MMV_TYPE_NOSUPPORT: + return PM_ERR_APPVERSION; + } + return 1; + } + + return 0; +} + +static void +mmv_reload_maybe(pmdaExt *pmda) +{ + int i; + struct stat s; + int need_reload = reload; + + /* check if generation numbers changed or monitored process exited */ + for (i = 0; i < scnt; i++) { + mmv_disk_header_t *hdr = (mmv_disk_header_t *)slist[i].addr; + if (hdr->g1 != slist[i].gen || hdr->g2 != slist[i].gen) { + need_reload++; + break; + } + if (slist[i].pid && !__pmProcessExists(slist[i].pid)) { + need_reload++; + break; + } + } + + /* + * check if the directory has been modified, reload if so; + * note modification may involve removal or newly appeared, + * a change in permissions from accessible to not (or vice- + * versa), and so on. + */ + if (stat(statsdir, &s) >= 0) { + if (s.st_mtime != statsdir_ts) { + need_reload++; + statsdir_code = 0; + statsdir_ts = s.st_mtime; + } + } else { + i = oserror(); + if (statsdir_code != i) { + statsdir_code = i; + statsdir_ts = 0; + need_reload++; + } + } + + if (need_reload) { + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, "MMV: %s: reloading", pmProgname); + map_stats(pmda); + + pmda->e_indoms = indoms; + pmda->e_nindoms = incnt; + pmdaRehash(pmda, metrics, mcnt); + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_DEBUG, + "MMV: %s: %d metrics and %d indoms after reload", + pmProgname, mcnt, incnt); + } +} + +/* Intercept request for descriptor and check if we'd have to reload */ +static int +mmv_desc(pmID pmid, pmDesc *desc, pmdaExt *ep) +{ + mmv_reload_maybe(ep); + return pmdaDesc(pmid, desc, ep); +} + +static int +mmv_text(int ident, int type, char **buffer, pmdaExt *ep) +{ + if (type & PM_TEXT_INDOM) + return PM_ERR_TEXT; + + mmv_reload_maybe(ep); + if (pmid_cluster(ident) == 0) { + if (pmid_item(ident) == 0) { + static char reloadoneline[] = "Control maps reloading"; + static char reloadtext[] = +"Writing anything other then 0 to this metric will result in\n" +"re-reading directory and re-mapping files.\n"; + + *buffer = (type & PM_TEXT_ONELINE) ? reloadoneline : reloadtext; + return 0; + } + else if (pmid_item(ident) == 1) { + static char debugoneline[] = "Debug flag"; + static char debugtext[] = +"See pmdbg(1). pmstore into this metric to change the debug value.\n"; + + *buffer = (type & PM_TEXT_ONELINE) ? debugoneline : debugtext; + return 0; + } + else + return PM_ERR_PMID; + } + else { + mmv_disk_string_t * str; + mmv_disk_metric_t * m; + mmv_disk_value_t * v; + stats_t * s; + + if (mmv_lookup_stat_metric_value(ident, PM_IN_NULL, &s, &m, &v) != 0) + return PM_ERR_PMID; + + if ((type & PM_TEXT_ONELINE) && m->shorttext) { + str = (mmv_disk_string_t *)((char *)s->addr + m->shorttext); + *buffer = str->payload; + return 0; + } + if ((type & PM_TEXT_HELP) && m->helptext) { + str = (mmv_disk_string_t *)((char *)s->addr + m->helptext); + *buffer = str->payload; + return 0; + } + } + + return PM_ERR_TEXT; +} + +static int +mmv_instance(pmInDom indom, int inst, char *name, + __pmInResult **result, pmdaExt *ep) +{ + mmv_reload_maybe(ep); + return pmdaInstance(indom, inst, name, result, ep); +} + +static int +mmv_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +static int +mmv_store(pmResult *result, pmdaExt *ep) +{ + int i, m; + + mmv_reload_maybe(ep); + + for (i = 0; i < result->numpmid; i++) { + pmValueSet * vsp = result->vset[i]; + __pmID_int * id = (__pmID_int *)&vsp->pmid; + + if (id->cluster == 0) { + for (m = 0; m < mcnt; m++) { + __pmID_int * mid = (__pmID_int *)&(metrics[m].m_desc.pmid); + + if (mid->cluster == 0 && mid->item == id->item) { + pmAtomValue atom; + int sts; + + if (vsp->numval != 1 ) + return PM_ERR_CONV; + + if ((sts = pmExtractValue(vsp->valfmt, &vsp->vlist[0], + PM_TYPE_32, &atom, PM_TYPE_32)) < 0) + return sts; + if (id->item == 0) + reload = atom.l; + else if (id->item == 1) + pmDebug = atom.l; + else + return PM_ERR_PERMISSION; + } + } + } + else + return PM_ERR_PERMISSION; + } + return 0; +} + +static int +mmv_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaTreePMID(pmns, name, pmid); +} + +static int +mmv_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaTreeName(pmns, pmid, nameset); +} + +static int +mmv_children(const char *name, int traverse, char ***kids, int **sts, pmdaExt *pmda) +{ + mmv_reload_maybe(pmda); + return pmdaTreeChildren(pmns, name, traverse, kids, sts); +} + +void +__PMDA_INIT_CALL +mmv_init(pmdaInterface *dp) +{ + int m; + int sep = __pmPathSeparator(); + + if (isDSO) { + pmdaDSO(dp, PMDA_INTERFACE_4, "mmv", NULL); + } else { + __pmSetProcessIdentity(username); + } + + pcptmpdir = pmGetConfig("PCP_TMP_DIR"); + pcpvardir = pmGetConfig("PCP_VAR_DIR"); + pcppmdasdir = pmGetConfig("PCP_PMDAS_DIR"); + + snprintf(statsdir, sizeof(statsdir), "%s%c%s", pcptmpdir, sep, prefix); + snprintf(pmnsdir, sizeof(pmnsdir), "%s%c" "pmns", pcpvardir, sep); + statsdir[sizeof(statsdir)-1] = '\0'; + pmnsdir[sizeof(pmnsdir)-1] = '\0'; + + /* Initialize internal dispatch table */ + if (dp->status == 0) { + /* + * number of hard-coded metrics here has to match initializer + * cases below, and pmns initialization in map_stats() + */ + mcnt = 2; + if ((metrics = malloc(mcnt*sizeof(pmdaMetric))) != NULL) { + /* + * all the hard-coded metrics have the same semantics + */ + for (m = 0; m < mcnt; m++) { + if (m == 0) + metrics[m].m_user = &reload; + else if (m == 1) + metrics[m].m_user = &pmDebug; + metrics[m].m_desc.pmid = pmid_build(dp->domain, 0, m); + metrics[m].m_desc.type = PM_TYPE_32; + metrics[m].m_desc.indom = PM_INDOM_NULL; + metrics[m].m_desc.sem = PM_SEM_INSTANT; + memset(&metrics[m].m_desc.units, 0, sizeof(pmUnits)); + } + } else { + __pmNotifyErr(LOG_ERR, "%s: pmdaInit - out of memory\n", + pmProgname); + if (isDSO) + return; + exit(0); + } + + dp->version.four.fetch = mmv_fetch; + dp->version.four.store = mmv_store; + dp->version.four.desc = mmv_desc; + dp->version.four.text = mmv_text; + dp->version.four.instance = mmv_instance; + dp->version.four.pmid = mmv_pmid; + dp->version.four.name = mmv_name; + dp->version.four.children = mmv_children; + pmdaSetFetchCallBack(dp, mmv_fetchCallBack); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); + pmdaInit(dp, indoms, incnt, metrics, mcnt); + } +} + +int +main(int argc, char **argv) +{ + char logfile[32]; + pmdaInterface dispatch = { 0 }; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + if (strncmp(pmProgname, "pmda", 4) == 0 && strlen(pmProgname) > 4) + prefix = pmProgname + 4; + snprintf(logfile, sizeof(logfile), "%s.log", prefix); + pmdaDaemon(&dispatch, PMDA_INTERFACE_4, pmProgname, MMV, logfile, NULL); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + mmv_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/mmv/src/root_mmv b/src/pmdas/mmv/src/root_mmv new file mode 100644 index 0000000..28c810a --- /dev/null +++ b/src/pmdas/mmv/src/root_mmv @@ -0,0 +1,13 @@ +/* + * MMV metrics name space + */ + +#ifndef MMV +#define MMV 70 +#endif + +root { + mmv MMV:*:* +} + +#undef MMV |