diff options
Diffstat (limited to 'src/libpcp_mmv')
-rw-r--r-- | src/libpcp_mmv/GNUmakefile | 35 | ||||
-rw-r--r-- | src/libpcp_mmv/src/GNUmakefile | 73 | ||||
-rw-r--r-- | src/libpcp_mmv/src/exports | 26 | ||||
-rw-r--r-- | src/libpcp_mmv/src/mmv_stats.c | 628 |
4 files changed, 762 insertions, 0 deletions
diff --git a/src/libpcp_mmv/GNUmakefile b/src/libpcp_mmv/GNUmakefile new file mode 100644 index 0000000..ef4485e --- /dev/null +++ b/src/libpcp_mmv/GNUmakefile @@ -0,0 +1,35 @@ +# +# Copyright (C) 2001,2009 Silicon Graphics, Inc. All Rights Reserved. +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This library 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 Lesser General Public +# License for more details. + +# +# libpcp_mmv.so - performance data export through shared memory +# (used in conjunction with the mmv pmda) +# + +TOPDIR = ../.. + +include $(TOPDIR)/src/include/builddefs + +BASE = libpcp_mmv.a + +SUBDIRS = src + +default install: $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +include $(BUILDRULES) + +default_pcp: default + +install_pcp: install + diff --git a/src/libpcp_mmv/src/GNUmakefile b/src/libpcp_mmv/src/GNUmakefile new file mode 100644 index 0000000..a60029c --- /dev/null +++ b/src/libpcp_mmv/src/GNUmakefile @@ -0,0 +1,73 @@ +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 2001,2009 Silicon Graphics, Inc. All Rights Reserved. +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This library 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 Lesser General Public +# License for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs +-include ./GNUlocaldefs + +CFILES = mmv_stats.c +VERSION_SCRIPT = exports +LSRCFILES = $(VERSION_SCRIPT) + +STATICLIBTARGET = libpcp_mmv.a +DSOVERSION = 1 +LIBTARGET = libpcp_mmv.$(DSOSUFFIX).$(DSOVERSION) +SYMTARGET = libpcp_mmv.$(DSOSUFFIX) +ifeq "$(TARGET_OS)" "darwin" +LIBTARGET = libpcp_mmv.$(DSOVERSION).$(DSOSUFFIX) +endif +ifeq "$(TARGET_OS)" "mingw" +LIBTARGET = libpcp_mmv.$(DSOSUFFIX) +SYMTARGET = +STATICLIBTARGET = +endif +ifeq "$(ENABLE_SHARED)" "no" +LIBTARGET = +SYMTARGET = +endif + +LCFLAGS = -I. +LLDLIBS = -lpcp +LDIRT = $(SYMTARGET) + +default: $(LIBTARGET) $(SYMTARGET) $(STATICLIBTARGET) + +include $(BUILDRULES) + +install: default +ifneq ($(LIBTARGET),) + $(INSTALL) -m 755 $(LIBTARGET) $(PCP_LIB_DIR)/$(LIBTARGET) +endif +ifneq ($(SYMTARGET),) + for tt in $(SYMTARGET); do \ + $(INSTALL) -S $(LIBTARGET) $(PCP_LIB_DIR)/$$tt || exit 1; \ + done +endif +ifneq ($(STATICLIBTARGET),) + $(INSTALL) -m 755 $(STATICLIBTARGET) $(PCP_LIB_DIR)/$(STATICLIBTARGET) +endif + +default_pcp: default + +install_pcp: install + +ifneq ($(SYMTARGET),) +$(SYMTARGET): + $(LN_S) -f $(LIBTARGET) $@ +endif + +ifneq ($(LIBTARGET),) +$(LIBTARGET): $(VERSION_SCRIPT) +endif diff --git a/src/libpcp_mmv/src/exports b/src/libpcp_mmv/src/exports new file mode 100644 index 0000000..00b4fc6 --- /dev/null +++ b/src/libpcp_mmv/src/exports @@ -0,0 +1,26 @@ +PCP_MMV_1.0 { + global: + mmv_stats_init; + mmv_stats_stop; + + mmv_inc_value; + mmv_set_value; + mmv_set_string; + + mmv_lookup_value_desc; + + mmv_stats_add; + mmv_stats_add_fallback; + + mmv_stats_inc; + mmv_stats_inc_fallback; + + mmv_stats_interval_end; + mmv_stats_interval_start; + + mmv_stats_set; + mmv_stats_set_string; + mmv_stats_set_strlen; + + local: *; +}; diff --git a/src/libpcp_mmv/src/mmv_stats.c b/src/libpcp_mmv/src/mmv_stats.c new file mode 100644 index 0000000..a66d06f --- /dev/null +++ b/src/libpcp_mmv/src/mmv_stats.c @@ -0,0 +1,628 @@ +/* + * Memory Mapped Values PMDA Client API + * + * Copyright (C) 2001,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 Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + */ +#include "pmapi.h" +#include <sys/stat.h> +#include "mmv_stats.h" +#include "mmv_dev.h" +#include "impl.h" + +static void +mmv_stats_path(const char *fname, char *fullpath, size_t pathlen) +{ + int sep = __pmPathSeparator(); + + snprintf(fullpath, pathlen, "%s%c" "mmv" "%c%s", + pmGetConfig("PCP_TMP_DIR"), sep, sep, fname); +} + +static void * +mmv_mapping_init(const char *fname, size_t size) +{ + char path[MAXPATHLEN]; + void *addr = NULL; + mode_t cur_umask; + int fd, sts = 0; + + /* unlink+creat will cause the pmda to reload on next fetch */ + mmv_stats_path(fname, path, sizeof(path)); + unlink(path); + cur_umask = umask(S_IWGRP | S_IWOTH); + fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644); + umask(cur_umask); + if (fd < 0) + return NULL; + + if (ftruncate(fd, size) != -1) + addr = __pmMemoryMap(fd, size, 1); + else + sts = oserror(); + + close(fd); + setoserror(sts); + return addr; +} + +static int +mmv_singular(__int32_t indom) +{ + return (indom == 0 || indom == PM_INDOM_NULL); +} + +static mmv_disk_indom_t * +mmv_lookup_disk_indom(__int32_t indom, mmv_disk_indom_t *in, int nindoms) +{ + int i; + + for (i = 0; i < nindoms; i++) + if (in[i].serial == indom) + return &in[i]; + return NULL; +} + +static const mmv_indom_t * +mmv_lookup_indom(__int32_t indom, const mmv_indom_t *in, int nindoms) +{ + int i; + + for (i = 0; i < nindoms; i++) + if (in[i].serial == indom) + return &in[i]; + return NULL; +} + +static __uint64_t +mmv_generation(void) +{ + struct timeval now; + __uint32_t gen1, gen2; + + __pmtimevalNow(&now); + gen1 = now.tv_sec; + gen2 = now.tv_usec; + return (((__uint64_t)gen1 << 32) | (__uint64_t)gen2); +} + +void * +mmv_stats_init(const char *fname, + int cluster, mmv_stats_flags_t fl, + const mmv_metric_t *st, int nmetrics, + const mmv_indom_t *in, int nindoms) +{ + mmv_disk_instance_t *inlist; + mmv_disk_indom_t *domlist; + mmv_disk_metric_t *mlist; + mmv_disk_string_t *slist; + mmv_disk_value_t *vlist; + mmv_disk_header_t *hdr; + mmv_disk_toc_t *toc; + __uint64_t indoms_offset; /* anchor start of indoms section */ + __uint64_t instances_offset; /* anchor start of instances section */ + __uint64_t metrics_offset; /* anchor start of metrics section */ + __uint64_t values_offset; /* anchor start of values section */ + __uint64_t strings_offset; /* anchor start of any/all strings */ + void *addr; + size_t size; + int i, j, k, tocidx, stridx; + int ninstances = 0; + int nstrings = 0; + int nvalues = 0; + + for (i = 0; i < nindoms; i++) { + if (mmv_singular(in[i].serial)) { + setoserror(ESRCH); + return NULL; + } + ninstances += in[i].count; + if (in[i].shorttext) + nstrings++; + if (in[i].helptext) + nstrings++; + } + + for (i = 0; i < nmetrics; i++) { + if ((st[i].type < MMV_TYPE_NOSUPPORT) || + (st[i].type > MMV_TYPE_ELAPSED) || strlen(st[i].name) == 0) { + setoserror(EINVAL); + return NULL; + } + + if (st[i].helptext) + nstrings++; + if (st[i].shorttext) + nstrings++; + + if (!mmv_singular(st[i].indom)) { + const mmv_indom_t * mi; + + if ((mi = mmv_lookup_indom(st[i].indom, in, nindoms)) == NULL) { + setoserror(ESRCH); + return NULL; + } + if (st[i].type == MMV_TYPE_STRING) + nstrings += mi->count; + nvalues += mi->count; + } else { + if (st[i].type == MMV_TYPE_STRING) + nstrings++; + nvalues++; + } + } + + /* TOC follows header, with enough entries to hold */ + /* indoms, instances, metrics, values, and strings */ + size = sizeof(mmv_disk_toc_t) * 2; + if (nindoms) + size += sizeof(mmv_disk_toc_t) * 2; + if (nstrings) + size += sizeof(mmv_disk_toc_t) * 1; + indoms_offset = sizeof(mmv_disk_header_t) + size; + + /* Following the indom definitions are the actual instances */ + size = sizeof(mmv_disk_indom_t) * nindoms; + instances_offset = indoms_offset + size; + + /* Following the instances are the metric definitions */ + for (size = 0, i = 0; i < nindoms; i++) + size += in[i].count * sizeof(mmv_disk_instance_t); + metrics_offset = instances_offset + size; + + /* Following metric definitions are the actual values */ + size = nmetrics * sizeof(mmv_disk_metric_t); + values_offset = metrics_offset + size; + + /* Following the values are the string values and/or help text */ + size = nvalues * sizeof(mmv_disk_value_t); + strings_offset = values_offset + size; + + /* End of file follows all of the actual strings */ + size = strings_offset + nstrings * sizeof(mmv_disk_string_t); + + if ((addr = mmv_mapping_init(fname, size)) == NULL) + return NULL; + + /* + * We unconditionally clobber the stats file on each restart; + * easier this way and the clients deal with counter wraps. + * We also write from the start going forward through the file + * with an occassional step back to (re)write the TOC page - + * this gives the kernel a decent shot at laying out the file + * contiguously ondisk (hopefully we dont do much disk I/O on + * this file, but it will be written to disk at times so lets + * try to minimise that I/O traffic, eh?). + */ + + hdr = (mmv_disk_header_t *) addr; + strncpy(hdr->magic, "MMV", 4); + hdr->version = MMV_VERSION; + hdr->g1 = mmv_generation(); + hdr->g2 = 0; + hdr->tocs = 2; + if (nindoms) + hdr->tocs += 2; + if (nstrings) + hdr->tocs += 1; + hdr->flags = fl; + hdr->cluster = cluster; + hdr->process = (__int32_t)getpid(); + + toc = (mmv_disk_toc_t *)((char *)addr + sizeof(mmv_disk_header_t)); + tocidx = 0; + + if (nindoms) { + toc[tocidx].type = MMV_TOC_INDOMS; + toc[tocidx].count = nindoms; + toc[tocidx].offset = indoms_offset; + tocidx++; + toc[tocidx].type = MMV_TOC_INSTANCES; + toc[tocidx].count = ninstances; + toc[tocidx].offset = instances_offset; + tocidx++; + } + toc[tocidx].type = MMV_TOC_METRICS; + toc[tocidx].count = nmetrics; + toc[tocidx].offset = metrics_offset; + tocidx++; + toc[tocidx].type = MMV_TOC_VALUES; + toc[tocidx].count = nvalues; + toc[tocidx].offset = values_offset; + tocidx++; + if (nstrings) { + toc[tocidx].type = MMV_TOC_STRINGS; + toc[tocidx].count = nstrings; + toc[tocidx].offset = strings_offset; + tocidx++; + } + + /* Indom section */ + domlist = (mmv_disk_indom_t *)((char *)addr + indoms_offset); + for (i = 0; i < nindoms; i++) { + domlist[i].serial = in[i].serial; + domlist[i].count = in[i].count; + domlist[i].offset = 0; /* filled in below */ + domlist[i].shorttext = 0; /* filled in later */ + domlist[i].helptext = 0; /* filled in later */ + } + + /* Instances section */ + inlist = (mmv_disk_instance_t *)((char *)addr + instances_offset); + for (i = 0; i < nindoms; i++) { + mmv_instances_t *insts = in[i].instances; + domlist[i].offset = ((char *)inlist - (char *)addr); + for (j = 0; j < domlist[i].count; j++) { + inlist->indom = indoms_offset + (i * sizeof(mmv_disk_indom_t)); + inlist->padding = 0; + inlist->internal = insts[j].internal; + strncpy(inlist->external, insts[j].external, MMV_NAMEMAX); + inlist->external[MMV_NAMEMAX-1] = '\0'; + inlist++; + } + } + + /* Metrics section */ + mlist = (mmv_disk_metric_t *)((char *)addr + metrics_offset); + for (i = 0; i < nmetrics; i++) { + strncpy(mlist[i].name, st[i].name, MMV_NAMEMAX); + mlist[i].name[MMV_NAMEMAX-1] = '\0'; + mlist[i].item = st[i].item; + mlist[i].type = st[i].type; + mlist[i].indom = st[i].indom; + mlist[i].dimension = st[i].dimension; + mlist[i].semantics = st[i].semantics; + mlist[i].shorttext = 0; /* filled in later */ + mlist[i].helptext = 0; /* filled in later */ + mlist[i].padding = 0; + } + + /* Values section */ + vlist = (mmv_disk_value_t *)((char *)addr + values_offset); + for (i = j = 0; i < nmetrics; i++) { + __uint64_t off = metrics_offset + i * sizeof(mmv_disk_metric_t); + + if (mmv_singular(st[i].indom)) { + memset(&vlist[j], 0, sizeof(mmv_disk_value_t)); + vlist[j].metric = off; + j++; + } else { + __uint64_t ioff; + mmv_disk_indom_t *indom; + + indom = mmv_lookup_disk_indom(st[i].indom, domlist, nindoms); + for (k = 0; k < indom->count; k++) { + ioff = indom->offset + sizeof(mmv_disk_instance_t) * k; + memset(&vlist[j], 0, sizeof(mmv_disk_value_t)); + vlist[j].metric = off; + vlist[j].instance = ioff; + j++; + } + } + } + + /* Strings section */ + slist = (mmv_disk_string_t *)((char *)addr + strings_offset); + stridx = 0; + + /* + * 3 phases: all string values, any metric help, any indom help. + */ + for (i = 0; i < nvalues; i++) { + mmv_disk_metric_t * metric = (mmv_disk_metric_t *) + ((char *)(addr + vlist[i].metric)); + if (metric->type == MMV_TYPE_STRING) { + vlist[i].extra = strings_offset + + (stridx * sizeof(mmv_disk_string_t)); + stridx++; + } + } + for (i = 0; i < nmetrics; i++) { + if (st[i].shorttext) { + mlist[i].shorttext = strings_offset + + (stridx * sizeof(mmv_disk_string_t)); + strncpy(slist[stridx].payload, st[i].shorttext, MMV_STRINGMAX); + slist[stridx].payload[MMV_STRINGMAX-1] = '\0'; + stridx++; + } + if (st[i].helptext) { + mlist[i].helptext = strings_offset + + (stridx * sizeof(mmv_disk_string_t)); + strncpy(slist[stridx].payload, st[i].helptext, MMV_STRINGMAX); + slist[stridx].payload[MMV_STRINGMAX-1] = '\0'; + stridx++; + } + } + for (i = 0; i < nindoms; i++) { + if (in[i].shorttext) { + domlist[i].shorttext = strings_offset + + (stridx * sizeof(mmv_disk_string_t)); + strncpy(slist[stridx].payload, in[i].shorttext, MMV_STRINGMAX); + slist[stridx].payload[MMV_STRINGMAX-1] = '\0'; + stridx++; + } + if (in[i].helptext) { + domlist[i].helptext = strings_offset + + (stridx * sizeof(mmv_disk_string_t)); + strncpy(slist[stridx].payload, in[i].helptext, MMV_STRINGMAX); + slist[stridx].payload[MMV_STRINGMAX-1] = '\0'; + stridx++; + } + } + + /* Complete - unlock the header, PMDA can read now */ + hdr->g2 = hdr->g1; + + return addr; +} + +void +mmv_stats_stop(const char *fname, void *addr) +{ + mmv_disk_header_t *hdr = (mmv_disk_header_t *)addr; + char path[MAXPATHLEN]; + struct stat sbuf; + + mmv_stats_path(fname, path, sizeof(path)); + if (stat(path, &sbuf) < 0) + sbuf.st_size = (size_t)-1; + else if (hdr->flags & MMV_FLAG_PROCESS) + unlink(path); + __pmMemoryUnmap(addr, sbuf.st_size); +} + +pmAtomValue * +mmv_lookup_value_desc(void *addr, const char *metric, const char *inst) +{ + if (addr != NULL && metric != NULL) { + 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)); + + for (i = 0; i < hdr->tocs; i++) { + if (toc[i].type == MMV_TOC_VALUES) { + int j; + mmv_disk_value_t *v = (mmv_disk_value_t *) + ((char *)addr + toc[i].offset); + + for (j = 0; j < toc[i].count; j++) { + mmv_disk_metric_t *m = (mmv_disk_metric_t *) + ((char *)addr + v[j].metric); + if (strcmp(m->name, metric) == 0) { + if (mmv_singular(m->indom)) { /* Singular metric */ + return &v[j].value; + } else { + if (inst == NULL) { + /* Metric has multiple instances, but + * we don't know which one to return, + * so return an error + */ + return NULL; + } else { + mmv_disk_instance_t * in = + (mmv_disk_instance_t *) + ((char *)addr + v[j].instance); + if (strcmp(in->external, inst) == 0) + return &v[j].value; + } + } + } + } + } + } + } + + return NULL; +} + +void +mmv_inc_value(void *addr, pmAtomValue *av, double inc) +{ + if (av != NULL && addr != NULL) { + mmv_disk_value_t * v = (mmv_disk_value_t *) av; + mmv_disk_metric_t * m = (mmv_disk_metric_t *) + ((char *)addr + v->metric); + switch (m->type) { + case MMV_TYPE_I32: + v->value.l += (__int32_t)inc; + break; + case MMV_TYPE_U32: + v->value.ul += (__uint32_t)inc; + break; + case MMV_TYPE_I64: + v->value.ll += (__int64_t)inc; + break; + case MMV_TYPE_U64: + v->value.ull += (__uint64_t)inc; + break; + case MMV_TYPE_FLOAT: + v->value.f += (float)inc; + break; + case MMV_TYPE_DOUBLE: + v->value.d += inc; + break; + case MMV_TYPE_ELAPSED: + if (inc < 0) + v->extra = (__int64_t)inc; + else { + v->value.ll += v->extra + (__int64_t)inc; + v->extra = 0; + } + break; + default: + break; + } + } +} + +void +mmv_set_value(void *addr, pmAtomValue *av, double val) +{ + if (av != NULL && addr != NULL) { + mmv_disk_value_t * v = (mmv_disk_value_t *) av; + mmv_disk_metric_t * m = (mmv_disk_metric_t *) + ((char *)addr + v->metric); + switch (m->type) { + case MMV_TYPE_I32: + v->value.l = (__int32_t)val; + break; + case MMV_TYPE_U32: + v->value.ul = (__uint32_t)val; + break; + case MMV_TYPE_I64: + v->value.ll = (__int64_t)val; + break; + case MMV_TYPE_U64: + v->value.ull = (__uint64_t)val; + break; + case MMV_TYPE_FLOAT: + v->value.f = (float)val; + break; + case MMV_TYPE_DOUBLE: + v->value.d = val; + break; + case MMV_TYPE_ELAPSED: + v->value.ll = (__int64_t)val; + v->extra = 0; + break; + default: + break; + } + } +} + +void +mmv_set_string(void *addr, pmAtomValue *av, const char *string, int size) +{ + if (av != NULL && addr != NULL && string != NULL) { + mmv_disk_value_t * v = (mmv_disk_value_t *) av; + mmv_disk_metric_t * m = (mmv_disk_metric_t *) + ((char *)addr + v->metric); + + if (m->type == MMV_TYPE_STRING && + (size >= 0 && size < MMV_STRINGMAX - 1)) { + __uint64_t soffset = v->extra; + mmv_disk_string_t * s; + + s = (mmv_disk_string_t *)((char *)addr + soffset); + strncpy(s->payload, string, size); + s->payload[size] = '\0'; + v->value.l = size; + } + } +} + +/* + * Simple wrapper routines + */ + +void +mmv_stats_add(void *addr, + const char *metric, const char *instance, double count) +{ + if (addr) { + pmAtomValue * mmv_metric; + mmv_metric = mmv_lookup_value_desc(addr, metric, instance); + if (mmv_metric) + mmv_inc_value(addr, mmv_metric, count); + } +} + +void +mmv_stats_inc(void *addr, const char *metric, const char *instance) +{ + mmv_stats_add(addr, metric, instance, 1); +} + +void +mmv_stats_set(void *addr, + const char *metric, const char *instance, double value) +{ + if (addr) { + pmAtomValue * mmv_metric; + mmv_metric = mmv_lookup_value_desc(addr, metric, instance); + if (mmv_metric) + mmv_set_value(addr, mmv_metric, value); + } +} + +void +mmv_stats_add_fallback(void *addr, const char *metric, + const char *instance, const char *instance2, double count) +{ + if (addr) { + pmAtomValue * mmv_metric; + mmv_metric = mmv_lookup_value_desc(addr, metric, instance); + if (mmv_metric == NULL) + mmv_metric = mmv_lookup_value_desc(addr,metric,instance2); + if (mmv_metric) + mmv_inc_value(addr, mmv_metric, count); + } +} + +void +mmv_stats_inc_fallback(void *addr, const char *metric, + const char *instance, const char *instance2) +{ + mmv_stats_add_fallback(addr, metric, instance, instance2, 1); +} + +pmAtomValue * +mmv_stats_interval_start(void *addr, pmAtomValue *value, + const char *metric, const char *instance) +{ + if (addr) { + if (value == NULL) + value = mmv_lookup_value_desc(addr, metric, instance); + if (value) { + struct timeval tv; + __pmtimevalNow(&tv); + mmv_inc_value(addr, value, -(tv.tv_sec*1e6 + tv.tv_usec)); + } + } + return value; +} + +void +mmv_stats_interval_end(void *addr, pmAtomValue *value) +{ + if (value && addr) { + struct timeval tv; + __pmtimevalNow(&tv); + mmv_inc_value(addr, value, (tv.tv_sec*1e6 + tv.tv_usec)); + } +} + +void +mmv_stats_set_string(void *addr, const char *metric, + const char *instance, const char *string) +{ + if (addr) { + size_t len = strlen(string); + pmAtomValue *mmv_metric; + mmv_metric = mmv_lookup_value_desc(addr, metric, instance); + mmv_set_string(addr, mmv_metric, string, len); + } +} + +void +mmv_stats_set_strlen(void *addr, const char *metric, + const char *instance, const char *string, size_t len) +{ + if (addr) { + pmAtomValue *mmv_metric; + mmv_metric = mmv_lookup_value_desc(addr, metric, instance); + mmv_set_string(addr, mmv_metric, string, len); + } +} |