diff --git a/configure.ac b/configure.ac index 253372e39..49b63c150 100644 --- a/configure.ac +++ b/configure.ac @@ -925,6 +925,12 @@ AS_IF([test "x$enable_slingshot" = xyes],[ [AC_MSG_ERROR([libcxi or its headers not found])]) ]) +AC_ARG_ENABLE([slingshot_switch], + [AS_HELP_STRING([--enable-slingshot_switch], [require the slinghost on-switch plugins @<:@default=check@:>@])], + [], + [enable_slingshot_switch="check"]) +AM_CONDITIONAL([ENABLE_SLINGSHOT_SWITCH], [test "x$enable_slingshot_switch" != xno]) + # define substitutions for configvars and other sed-generated files. # note carefully the escapes. OVIS_DO_SUBST([LDMS_SUBST_RULE], ["sed \ @@ -1097,6 +1103,7 @@ ldms/src/sampler/syspapi/Makefile ldms/src/sampler/app_sampler/Makefile ldms/src/sampler/slingshot_metrics/Makefile ldms/src/sampler/slingshot_info/Makefile +ldms/src/sampler/slingshot_switch/Makefile ldms/src/contrib/sampler/Makefile ldms/src/contrib/sampler/daos/Makefile ldms/src/contrib/sampler/daos/test/Makefile diff --git a/ldms/src/sampler/Makefile.am b/ldms/src/sampler/Makefile.am index 59e8f5b54..96675f154 100644 --- a/ldms/src/sampler/Makefile.am +++ b/ldms/src/sampler/Makefile.am @@ -308,3 +308,7 @@ SUBDIRS += slingshot_metrics SUBDIRS += slingshot_info endif endif + +if ENABLE_SLINGSHOT_SWITCH +SUBDIRS += slingshot_switch +endif diff --git a/ldms/src/sampler/slingshot_switch/Makefile.am b/ldms/src/sampler/slingshot_switch/Makefile.am new file mode 100644 index 000000000..9c39e2d1d --- /dev/null +++ b/ldms/src/sampler/slingshot_switch/Makefile.am @@ -0,0 +1,25 @@ +libslingshot_switch_la_SOURCES = \ + slingshot_switch.c +libslingshot_switch_la_LIBADD = \ + $(top_builddir)/ldms/src/core/libldms.la \ + $(top_builddir)/lib/src/coll/libcoll.la \ + $(top_builddir)/ldms/src/sampler/libsampler_base.la +libslingshot_switch_la_LDFLAGS = \ + -no-undefined \ + -export-symbols-regex 'get_plugin' +libslingshot_switch_la_CPPFLAGS = @OVIS_INCLUDE_ABS@ + +libslingshot_switch_1_la_SOURCES = \ + slingshot_switch_1.c +libslingshot_switch_1_la_LIBADD = \ + $(top_builddir)/ldms/src/core/libldms.la \ + $(top_builddir)/lib/src/coll/libcoll.la \ + $(top_builddir)/ldms/src/sampler/libsampler_base.la +libslingshot_switch_1_la_LDFLAGS = \ + -no-undefined \ + -export-symbols-regex 'get_plugin' +libslingshot_switch_1_la_CPPFLAGS = @OVIS_INCLUDE_ABS@ + +pkglib_LTLIBRARIES = libslingshot_switch.la libslingshot_switch_1.la + +dist_man7_MANS = Plugin_slingshot_switch.man diff --git a/ldms/src/sampler/slingshot_switch/Plugin_slingshot_switch.man b/ldms/src/sampler/slingshot_switch/Plugin_slingshot_switch.man new file mode 100644 index 000000000..1f7c0674c --- /dev/null +++ b/ldms/src/sampler/slingshot_switch/Plugin_slingshot_switch.man @@ -0,0 +1,131 @@ +\" Manpage for Plugin_slingshot_switch +.\" Contact ovis-help@ca.sandia.gov to correct errors or typos. +.TH man 7 "17 Nov 2023" "v4" "LDMS Plugin slingshot_switch man page" + +.SH NAME +Plugin_slingshot_switch - man page for the LDMS slingshot_switch plugin + +.SH SYNOPSIS +Within ldmsd_controller or a configuration file: +.br +config name=slingshot_switch [ = ] + +.SH DESCRIPTION +With LDMS (Lightweight Distributed Metric Service), plugins for the ldmsd (ldms +daemon) are configured via ldmsd_controller or a configuration file. The +slingshot_switch plugin uses LDMS_V_LIST and LDMS_V_RECORD to provide slingshot +switch info via the dump_counters command run on the switch. + +slingshot_switch and slingshot_switch_1 are the same plugins. There are two copies +to enable sampling at two different rates. + +.SH CONFIGURATION ATTRIBUTE SYNTAX +The slingshot_switch plugin uses the sampler_base base class. This man page covers +only the configuration attributes, or those with default values, specific to the +this plugin; see ldms_sampler_base.man for the attributes of the base class. + +.TP +.BR config +name= +.br +configuration line +.RS +.TP +name= +.br +This MUST be slingshot_switch (or slingshot_switch_1). +.TP +conffile= +.br +Configuration file. First non-comment line must be "n=XXX" or "p=XXX,YYY,ZZZ". +p does not support ranges. Then variables or groups are listed one +per line. Comments lines can be in the file designated by the first line +being a '#'. + +Arguments are those of dump_counters. +.TP +schema= +.br +Optional schema name. It is intended that the same sampler on different nodes +with different metrics have a different schema. If not specified, will +default to `slingshot_switch` (or `slingshot_switch_1`). +.RE + +.SH BUGS (and future enhancements) +\[bu] This is still under development. + +\[bu] Does not yet support ranges for the ports. + +\[bu] Does not check for duplicate ports. + +\[bu] Could have more robust handling of errors in the config file. + +\[bu] MAX Ports is 70. + +\[bu] Possibly can reduce unnecessary allocations in schema_metric_list. + +\[bu] DEBUG messages are excessive, while this is in development. + +\[bu] Need to check for extra whitespace in variable names. + +\[bu] Only checking for the expected number of data output lines. Note that the output has at least one extra line. + + +.SH EXAMPLES +.PP +1) Within ldmsd_controller or a configuration file: +.nf +load name=slingshot_switch +config name=slingshot_switch producer=vm1_1 instance=vm1_1/slingshot_switch conffile=/home/confffile.txt +start name=slingshot_switch interval=1000000 offset=0 +.fi +or the above with `slingshot_switch_1`. + +conffile.txt can look something like: +.nf +#This can be a leading comment(s) +n=65 +# This can be an interspersed comment(s) +cfrx +#This is yet another comment(s) +.fi + +.PP +2) For confile sampler_ss.conf: +.nf +env SWITCH=$(hostname) +env COMPONENT_ID=1 + +load name=slingshot_switch +config name=slingshot_switch producer=${SWITCH} component_id=${COMPONENT_ID} instance=${SWITCH}/port_metrics conffile=/rwfs/OVIS_slingshot-4.4.1/etc/ldms/slingshot_ldms_1s.txt +start name=slingshot_switch interval=1000000 +.fi + +with slingshot_ldms_1s.txt: +.nf +p=0,1,2,3 +rfc_3635 +.fi + +Command line to start ldmsd using the above: +.nf +/rwfs/OVIS_slingshot-4.4.1/etc/ldms# ldmsd -x sock:411 -c /rwfs/OVIS_slingshot-4.4.1/etc/ldms/sampler_ss.conf -m 2M -v QUIET +.fi + +Then ldms_ls output: +.nf +x3000c0r42b0/port_metrics1: consistent, last update: Fri Nov 17 17:24:08 2023 +0000 [23292us] +M u64 component_id 1 +D u64 job_id 0 +D u64 app_id 0 +M record_type slingshot_port LDMS_V_RECORD_TYPE +D list<> slingshot_port_list + port (x) IfInDiscards (x) IfInErrors (x) IfInUnknownProtos (x) IfOutDiscards (x) IfOutErrors (x) Dot3HCInPauseFrames (x) Dot3HCOutPauseFrames (x) IfHCInOctets (x) IfHCInUcastPkts (x) IfHCInMulticastPkts (x) IfHCInBroadcastPkts (x) IfHCOutOctets (x) IfHCOutUcastPkts (x) IfHCOutMulticastPkts (x) IfHCOutBroadcastPkts (x) + 0 3135102637 0 3135102637 0 0 0 0 147205648261491 2495004354147 2471 0 1536216301 20234005 0 0 + 1 3135102637 0 3135102637 0 0 0 0 147204949152872 2494992497033 0 0 698442077 10279716 0 0 + 2 3135102637 0 3135102637 0 0 0 0 147205081815556 2494994737508 0 0 698345785 10272362 0 0 + 3 3135102637 0 3135102637 0 0 0 0 147205460019681 2495001153446 0 0 698845184 10277326 0 0 +.fi + +.SH SEE ALSO +ldmsd(8), ldms_quickstart(7), ldmsd_controller(8), ldms_sampler_base(7) diff --git a/ldms/src/sampler/slingshot_switch/slingshot_switch.c b/ldms/src/sampler/slingshot_switch/slingshot_switch.c new file mode 100644 index 000000000..b613b2984 --- /dev/null +++ b/ldms/src/sampler/slingshot_switch/slingshot_switch.c @@ -0,0 +1,712 @@ +/* -*- c-basic-offset: 8 -*- + * Copyright (c) 2010-2016,2018,2022-2023 National Technology & Engineering + * Solutions of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 + * with NTESS, the U.S. Government retains certain rights in this software. + * Copyright (c) 2010-2016,2018,2022-2023 Open Grid Computing, Inc. All rights + * reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the BSD-type + * license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of Sandia nor the names of any contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Neither the name of Open Grid Computing nor the names of any + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Modified source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file slingshot_switch.c + * \brief slingshot switch data provider + * + * This sampler uses the dump_counters command to get data. + * It uses \c LDMS_V_LIST and \c LDMS_V_RECORD in the same way as + * \c procnetdev2. + * + * CONFIG file format: + * p = comma separated list (no range yet) OR n = number + * list of variables one per line or a group that it will parse + * + * interspersed comment lines (including first line) indicated by '#' + * as the first char in a line + * + * GOAL is to construct the command line for dump counters + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ldms.h" +#include "ldmsd.h" +#include "../sampler_base.h" + +#ifndef ARRAY_LEN +#define ARRAY_LEN(a) (sizeof(a) / sizeof(*a)) +#endif + +#define DUMPCOUNTERSLINE_MAX 2048 +#define SS_VARNAME_MAX 128 +#define SS_LINE_MAX 1024 +//NOTE: avoid MAX in future versions +#define SS_PORT_MAX 70 +static char commandline[DUMPCOUNTERSLINE_MAX]; +static char commandlineports[DUMPCOUNTERSLINE_MAX]; +static char commandlinevars[DUMPCOUNTERSLINE_MAX]; +static int commandline_ok = 0; +static int numvars = 0; /* NOTE: not keeping track of which ones */ +static int numports = 0; /* NOTE: not keeping track of which ones */ + +static int rec_metric_id_port; + +static int rec_def_idx; +static int ssport_list_mid; +static size_t rec_heap_sz; + +#define SAMP "slingshot_switch" +static ldmsd_msg_log_f msglog; +static base_data_t base; + + + +/* strip leading and trailing whitespace -- TAKEN FROM IBMAD_RECORDS_SAMPLER*/ +static void strip_whitespace(char **start) +{ + /* strip leading whitespace */ + while (isspace(**start)) { + (*start)++; + } + + /* strip trailing whitespace */ + char * last; + last = *start + strlen(*start) - 1; + while (last > *start) { + if (isspace(last[0])) { + last--; + } else { + break; + } + } + last[1] = '\0'; +} + +static int buildCommandLinePorts(char* lbuf){ + char plist[SS_LINE_MAX]; + char *pcpy; + char *pch = NULL; + char *saveptr = NULL; + int n; + int i; + int rc; + + + rc = sscanf(lbuf, "p=%s\n", plist); + if (rc != 1){ + /* then parse for n */ + rc = sscanf(lbuf, "n=%d\n", &n); + if (rc != 1){ + msglog(LDMSD_LERROR, + SAMP ": Bad format in file. First line MUST be p=XXX or n=XXX\n"); + return EINVAL; + } + if ((n < 0) || (n > SS_PORT_MAX)) { + msglog(LDMSD_LERROR, + SAMP ": ERROR: Bad port value '%d'\n", n); + return EINVAL; + } + numports = n+1; /* will start at 0 */ + snprintf(commandlineports, sizeof(commandlineports), + " -n %d", n ); + return 0; + } + + /* parse for p */ + strncpy(commandlineports, "", sizeof(commandlineports)); + pcpy = strdup(plist); + if (!pcpy){ + msglog(LDMSD_LERROR, SAMP ": Out of memory\n"); + rc = ENOMEM; + goto err; + } + + pch = strtok_r(pcpy, ",", &saveptr); + while (pch != NULL){ + /* NOTE: concatinates a range expression to the first val only */ + rc = sscanf(pch, "%d", &i); + if (rc != 1){ + msglog(LDMSD_LERROR, + SAMP ": Bad format in file. p has '%s' within\n", + pch); + rc = EINVAL; + goto err; + } else if ((i < 0) || (i > SS_PORT_MAX)){ + msglog(LDMSD_LERROR, SAMP ": Bad port value '%d'\n", n); + rc = EINVAL; + goto err; + } + snprintf(lbuf, SS_LINE_MAX, " -p %d", i); + if (strlen(lbuf) + strlen(commandlineports) >= + sizeof(commandlineports)){ + msglog(LDMSD_LERROR, + SAMP ": port lengths will exceed MAX command line\n"); + rc = EINVAL; + goto err; + } + strcat(commandlineports, lbuf); + numports++; /* NOTE: not checking for duplicates */ + pch = strtok_r(NULL, ",", &saveptr); + } + rc = 0; + + err: + free(pcpy); + pcpy = NULL; + return rc; +} + + +static ldms_set_t get_set(struct ldmsd_sampler *self) +{ + if (base) + return base->set; + return NULL; +} + +static int create_metric_set(base_data_t base) +{ + ldms_schema_t schema; + ldms_record_t rec_def; + size_t heap_sz; + FILE *mf; + char cline[DUMPCOUNTERSLINE_MAX]; + char lbuf[SS_LINE_MAX]; + char varname[SS_VARNAME_MAX]; + int port; + char* varptr; + char* s; + unsigned long v; + int rc; + + /* make a 1 port output to parse */ + snprintf(cline, sizeof(cline), "dump_counters -p 1"); + if (strlen(cline) + strlen(commandlinevars) > sizeof(cline)){ + msglog(LDMSD_LERROR, + SAMP ": Varname lengths will exceed MAX command line\n"); + return EINVAL; + } + strcat(cline, commandlinevars); + + /* Create a metric set of the required size */ + schema = base_schema_new(base); + if (!schema) { + msglog(LDMSD_LERROR, + "%s: The schema '%s' could not be created, errno=%d.\n", + __FILE__, base->schema_name, errno); + rc = EINVAL; + goto err1; + } + + rec_def = ldms_record_create("slingshot_port"); + if (!rec_def){ + rc = EINVAL; + goto err1; + } + + /* NOTE: need val 'x' (not 0 nor NULL) */ + msglog(LDMSD_LDEBUG, + SAMP ": Adding record %d '%s' type LDMS_V_CHAR_ARRAY \n", + 0, "port"); + rc = ldms_record_metric_add(rec_def, "port", "x", LDMS_V_U64, 1); + if (rc < 0){ + /* errno is already set */ + goto err3; + } + rec_metric_id_port = rc; + msglog(LDMSD_LDEBUG, + SAMP ": Port metric id '%d'\n", rec_metric_id_port); + msglog(LDMSD_LDEBUG, + SAMP ": will be executing '%s' to get the var names\n", + cline); + numvars = 0; + + mf = popen(cline, "r"); + if (!mf) { + msglog(LDMSD_LERROR, SAMP ": Could not execute '%s'\n", cline); + rc = ENOENT; + goto err3; + } + /* first line is just the header */ + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s){ + msglog(LDMSD_LERROR, SAMP ": Could not skip first line.\n"); + rc = EINVAL; + goto err3; + } + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s) + break; + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + + rc = sscanf(lbuf,"%d,%[^,],%lu", &port, varname, &v); + if (rc != 3) { + /* NOTE: Treating like this is the last line. */ + rc = 0; + break; + } + varptr = varname; + strip_whitespace(&varptr); + + msglog(LDMSD_LDEBUG, + SAMP ": Adding record %d '%s' type U64 \n", + numvars++, varptr); + /* NOTE: need val 'x' (not 0 nor NULL) */ + rc = ldms_record_metric_add(rec_def, varptr, "x", + LDMS_V_U64, 1); + if (rc < 0) { + /* errno is already set */ + goto err3; + } + msglog(LDMSD_LDEBUG, SAMP ": Added metric id '%d'\n", rc); + } while(s); + rc = 0; + + if (numvars == 0){ + msglog(LDMSD_LERROR, SAMP ": No vars!\n"); + rc = EINVAL; + goto err3; + } else { + msglog(LDMSD_LDEBUG, + SAMP ": Added %d records + port\n", numvars); + } + + rec_heap_sz = ldms_record_heap_size_get(rec_def); + heap_sz = SS_PORT_MAX * ldms_record_heap_size_get(rec_def); + + /* Add record definition into the schema */ + rec_def_idx = ldms_schema_record_add(schema, rec_def); + if (rec_def_idx < 0) { + rc = -rec_def_idx; + goto err3; + } + + /* Add a list (of records) */ + /* NOTE: if this is being allocated, can unnecessary ones be avoided? */ + ssport_list_mid = ldms_schema_metric_list_add(schema, "slingshot_port_list", + NULL, heap_sz); + if (ssport_list_mid < 0) { + rc = -ssport_list_mid; + goto err2; + } + + base_set_new(base); + if (!base->set) { + rc = errno; + goto err2; + } + + if (mf) + pclose(mf); + mf = NULL; + + return 0; +err3: + /* Only manually delete rec_def when it has not yet been added + to the schema */ + ldms_record_delete(rec_def); +err2: + base_schema_delete(base); + base = NULL; +err1: + if (mf) + pclose(mf); + mf = NULL; + + return rc; +} + + +/** + * check for invalid flags, with particular emphasis on warning the user about + */ +static int config_check(struct attr_value_list *kwl, + struct attr_value_list *avl, void *arg) +{ + char *value; + int i; + + char* deprecated[]={"set"}; + + for (i = 0; i < ARRAY_LEN(deprecated); i++){ + value = av_value(avl, deprecated[i]); + if (value){ + msglog(LDMSD_LERROR, + SAMP ": config argument %s has been deprecated.\n", + deprecated[i]); + return EINVAL; + } + } + + return 0; +} + +static const char *usage(struct ldmsd_plugin *self) +{ + return "config name=" SAMP " conffile=\n" \ + BASE_CONFIG_USAGE \ + " full path for the configuration file\n"; +} + +static int config(struct ldmsd_plugin *self, + struct attr_value_list *kwl, struct attr_value_list *avl) +{ + FILE* mf; + char* conffile; + char lbuf[SS_LINE_MAX]; + char varname[SS_VARNAME_MAX]; + char *s; + char *ivalue = NULL; + void *arg = NULL; + int rc; + + rc = config_check(kwl, avl, arg); + if (rc != 0){ + return rc; + } + + if (base) { + msglog(LDMSD_LERROR, SAMP ": Set already created.\n"); + return EINVAL; + } + + /* process conffile */ + ivalue = av_value(avl, "conffile"); + if (!ivalue) { + msglog(LDMSD_LERROR, + SAMP ": A configuration file is required.\n"); + goto err1; + } + + conffile = strdup(ivalue); + msglog(LDMSD_LDEBUG, + SAMP ": Should be trying to process conffile '%s'\n", conffile); + mf = fopen(conffile, "r"); + if (!mf) { + msglog(LDMSD_LERROR, SAMP ": Could not open '%s'\n", conffile); + rc = ENOENT; + goto err1; + } + + fseek(mf, 0, SEEK_SET); /* seek should work if get to EOF */ + /* first non-comment line MUST be "p=XXX" or "n=XXX" */ + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s){ + msglog(LDMSD_LERROR, + SAMP ": Bad format in file. First line noncomment line MUST be p=XXX or n=XXX\n"); + rc = EINVAL; + goto err1; + } + if ((strlen(lbuf) > 1) && (lbuf[0] == '#')){ + msglog(LDMSD_LDEBUG, + SAMP ": skipping input <%s>\n", lbuf); + continue; + } + + break; /* Got a line to parse */ + } while (s); + + /* note command line will be incomplete after this */ + rc = buildCommandLinePorts(lbuf); + if (rc) + goto err1; + + /* variables: store them in the slist, then execute + a command on a single port to get all the values. */ + strncpy(commandlinevars, "", sizeof(commandlinevars)); + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s) + break; + rc = sscanf(lbuf," %s", varname); /* NOTE: check for extra whitespace */ + if ((rc != 1) || (strlen(varname) == 0) || (varname[0] == '#')){ + msglog(LDMSD_LDEBUG, + SAMP ": skipping input <%s>\n", lbuf); + continue; + } + snprintf(lbuf, sizeof(lbuf), " -s %s", varname); + if (strlen(lbuf) + strlen(commandlinevars) >= + sizeof(commandlinevars)){ + msglog(LDMSD_LERROR, + SAMP ": varname lengths will exceed MAX command line\n"); + rc = EINVAL; + goto err1; + } + strcat(commandlinevars, lbuf); + } while(s); + + if (mf) + fclose(mf); + mf = NULL; + + base = base_config(avl, SAMP, SAMP, msglog); + if (!base){ + rc = EINVAL; + goto err; + } + + rc = create_metric_set(base); + if (rc) { + msglog(LDMSD_LERROR, SAMP ": failed to create a metric set.\n"); + goto err; + } + + snprintf(commandline, sizeof(commandline), "dump_counters"); + if (strlen(commandline) + strlen(commandlineports) + strlen(commandlinevars) > sizeof(commandline)){ + msglog(LDMSD_LERROR, + SAMP ": Ports and vars lengths will exceed MAX command line\n"); + rc = EINVAL; + goto err; + } + + strcat(commandline, commandlineports); + strcat(commandline, commandlinevars); + commandline_ok = 1; + + free(conffile); + conffile = NULL; + return 0; + + err: + base_del(base); + + err1: + if (mf) + pclose(mf); + mf = NULL; + free(conffile); + conffile = NULL; + numvars = 0; + numports = 0; + commandline[0] = '\0'; + commandline_ok = 0; + return rc; +} + +static int sample(struct ldmsd_sampler *self) +{ + FILE* mf; + char lbuf[SS_LINE_MAX]; + int port; + union ldms_value vport; + char varname[SS_VARNAME_MAX]; + union ldms_value* v; + char *s; + int i,j ; + ldms_mval_t lh, rec_inst; + size_t heap_sz; + int rc; + + + msglog(LDMSD_LDEBUG, SAMP ": in sample\n"); + + if (!base){ + msglog(LDMSD_LDEBUG, SAMP ": plugin not initialized\n"); + return EINVAL; + } + + mf = popen(commandline, "r"); + if (!mf) { + msglog(LDMSD_LERROR, + SAMP ": Could not execute '%s'\n", commandline); + return ENOENT; + } + + v = calloc(numvars, sizeof(union ldms_value)); + if (!v){ + msglog(LDMSD_LERROR, SAMP ": Out of memory.\n"); + if (mf) + pclose(mf); + mf = NULL; + return ENOMEM; + } + +begin: + base_sample_begin(base); + + lh = ldms_metric_get(base->set, ssport_list_mid); + + /* reset device data */ + ldms_list_purge(base->set, lh); + + fseek(mf, 0, SEEK_SET); //seek should work if get to EOF + + /* first line is just the header */ + s = fgets(lbuf, sizeof(lbuf), mf); + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + if (!s){ + msglog(LDMSD_LERROR, SAMP ": Bad format in command output.\n"); + rc = EINVAL; + goto ERR; + } + + /* for each port for each set of variables..... */ + i = 0; /* ports */ + j = 0; /* vars. NOTE: 'port' is not one of them */ + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s) + break; + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + + rc = sscanf(lbuf,"%d,%[^,],%lu", &port, varname, &v[j].v_u64); + if (rc != 3) { + msglog(LDMSD_LERROR, + SAMP ": Bad output to command on port %d variable %d (%d): '%s'\n", + i, j, rc, lbuf); + rc = EINVAL; + goto ERR; + } + if (j == (numvars-1)){ + rec_inst = ldms_record_alloc(base->set, rec_def_idx); + if (!rec_inst) + goto resize; + vport.v_u64 = port; + msglog(LDMSD_LDEBUG, + SAMP ": Should be setting port to %lu\n", + vport.v_u64); + ldms_record_set_u64(rec_inst, rec_metric_id_port, + vport.v_u64); + for (j = 0; j < numvars; j++){ + msglog(LDMSD_LDEBUG, + SAMP ": Should be setting var %d metric %d to %lu\n", + j, rec_metric_id_port+j+1, v[j].v_u64); + ldms_record_set_u64(rec_inst, + rec_metric_id_port+j+1, + v[j].v_u64); + } + ldms_list_append_record(base->set, lh, rec_inst); + j = 0; + i++; + } else { + j++; + } + if (i == numports){ + msglog(LDMSD_LDEBUG, + SAMP ": read everything expected. breaking\n"); + break; + /* NOTE: not checking if there are extra lines. + There will be one extra in the output */ + } + } while(s); + + base_sample_end(base); + if (mf) + pclose(mf); + mf = NULL; + + return 0; + +resize: + + free(v); + + /* + * We intend to leave the set in the inconsistent state so that + * the aggregators are aware that some metrics have not been newly + * sampled. + */ + heap_sz = ldms_set_heap_size_get(base->set) + 2*rec_heap_sz; + base_set_delete(base); + base_set_new_heap(base, heap_sz); + if (!base->set) { + rc = errno; + ldmsd_log(LDMSD_LCRITICAL, SAMP " : Failed to create a set with " + "a bigger heap. Error %d\n", rc); + return rc; + } + goto begin; + + ERR: + free(v); + base_sample_end(base); + if (mf) + pclose(mf); + mf = NULL; + + return rc; + +} + + +static void term(struct ldmsd_plugin *self) +{ + numvars = 0; + numports = 0; + commandline[0] = '\0'; + commandline_ok = 0; + base_set_delete(base); + base_del(base); + base = NULL; +} + + +static struct ldmsd_sampler slingshot_switch_plugin = { + .base = { + .name = SAMP, + .type = LDMSD_PLUGIN_SAMPLER, + .term = term, + .config = config, + .usage = usage, + }, + .get_set = get_set, + .sample = sample, +}; + +struct ldmsd_plugin *get_plugin(ldmsd_msg_log_f pf) +{ + msglog = pf; + base = NULL; + return &slingshot_switch_plugin.base; +} diff --git a/ldms/src/sampler/slingshot_switch/slingshot_switch_1.c b/ldms/src/sampler/slingshot_switch/slingshot_switch_1.c new file mode 100644 index 000000000..225601991 --- /dev/null +++ b/ldms/src/sampler/slingshot_switch/slingshot_switch_1.c @@ -0,0 +1,715 @@ +/* -*- c-basic-offset: 8 -*- + * Copyright (c) 2010-2016,2018,2022-2023 National Technology & Engineering + * Solutions of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 + * with NTESS, the U.S. Government retains certain rights in this software. + * Copyright (c) 2010-2016,2018,2022-2023 Open Grid Computing, Inc. All rights + * reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the BSD-type + * license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of Sandia nor the names of any contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Neither the name of Open Grid Computing nor the names of any + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Modified source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file slingshot_switch_1.c + * \brief slingshot switch data provider + * + * This is the same as slingshot_switch in order to get another copy + * to run at a different rate. + * + * This sampler uses the dump_counters command to get data. + * It uses \c LDMS_V_LIST and \c LDMS_V_RECORD in the same way as + * \c procnetdev2. + * + * CONFIG file format: + * p = comma separated list (no range yet) OR n = number + * list of variables one per line or a group that it will parse + * + * interspersed comment lines (including first line) indicated by '#' + * as the first char in a line + * + * GOAL is to construct the command line for dump counters + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ldms.h" +#include "ldmsd.h" +#include "../sampler_base.h" + +#ifndef ARRAY_LEN +#define ARRAY_LEN(a) (sizeof(a) / sizeof(*a)) +#endif + +#define DUMPCOUNTERSLINE_MAX 2048 +#define SS_VARNAME_MAX 128 +#define SS_LINE_MAX 1024 +//NOTE: avoid MAX in future versions +#define SS_PORT_MAX 70 +static char commandline[DUMPCOUNTERSLINE_MAX]; +static char commandlineports[DUMPCOUNTERSLINE_MAX]; +static char commandlinevars[DUMPCOUNTERSLINE_MAX]; +static int commandline_ok = 0; +static int numvars = 0; /* NOTE: not keeping track of which ones */ +static int numports = 0; /* NOTE: not keeping track of which ones */ + +static int rec_metric_id_port; + +static int rec_def_idx; +static int ssport_list_mid; +static size_t rec_heap_sz; + +#define SAMP "slingshot_switch_1" +static ldmsd_msg_log_f msglog; +static base_data_t base; + + + +/* strip leading and trailing whitespace -- TAKEN FROM IBMAD_RECORDS_SAMPLER*/ +static void strip_whitespace(char **start) +{ + /* strip leading whitespace */ + while (isspace(**start)) { + (*start)++; + } + + /* strip trailing whitespace */ + char * last; + last = *start + strlen(*start) - 1; + while (last > *start) { + if (isspace(last[0])) { + last--; + } else { + break; + } + } + last[1] = '\0'; +} + +static int buildCommandLinePorts(char* lbuf){ + char plist[SS_LINE_MAX]; + char *pcpy; + char *pch = NULL; + char *saveptr = NULL; + int n; + int i; + int rc; + + + rc = sscanf(lbuf, "p=%s\n", plist); + if (rc != 1){ + /* then parse for n */ + rc = sscanf(lbuf, "n=%d\n", &n); + if (rc != 1){ + msglog(LDMSD_LERROR, + SAMP ": Bad format in file. First line MUST be p=XXX or n=XXX\n"); + return EINVAL; + } + if ((n < 0) || (n > SS_PORT_MAX)) { + msglog(LDMSD_LERROR, + SAMP ": ERROR: Bad port value '%d'\n", n); + return EINVAL; + } + numports = n+1; /* will start at 0 */ + snprintf(commandlineports, sizeof(commandlineports), + " -n %d", n ); + return 0; + } + + /* parse for p */ + strncpy(commandlineports, "", sizeof(commandlineports)); + pcpy = strdup(plist); + if (!pcpy){ + msglog(LDMSD_LERROR, SAMP ": Out of memory\n"); + rc = ENOMEM; + goto err; + } + + pch = strtok_r(pcpy, ",", &saveptr); + while (pch != NULL){ + /* NOTE: concatinates a range expression to the first val only */ + rc = sscanf(pch, "%d", &i); + if (rc != 1){ + msglog(LDMSD_LERROR, + SAMP ": Bad format in file. p has '%s' within\n", + pch); + rc = EINVAL; + goto err; + } else if ((i < 0) || (i > SS_PORT_MAX)){ + msglog(LDMSD_LERROR, SAMP ": Bad port value '%d'\n", n); + rc = EINVAL; + goto err; + } + snprintf(lbuf, SS_LINE_MAX, " -p %d", i); + if (strlen(lbuf) + strlen(commandlineports) >= + sizeof(commandlineports)){ + msglog(LDMSD_LERROR, + SAMP ": port lengths will exceed MAX command line\n"); + rc = EINVAL; + goto err; + } + strcat(commandlineports, lbuf); + numports++; /* NOTE: not checking for duplicates */ + pch = strtok_r(NULL, ",", &saveptr); + } + rc = 0; + + err: + free(pcpy); + pcpy = NULL; + return rc; +} + + +static ldms_set_t get_set(struct ldmsd_sampler *self) +{ + if (base) + return base->set; + return NULL; +} + +static int create_metric_set(base_data_t base) +{ + ldms_schema_t schema; + ldms_record_t rec_def; + size_t heap_sz; + FILE *mf; + char cline[DUMPCOUNTERSLINE_MAX]; + char lbuf[SS_LINE_MAX]; + char varname[SS_VARNAME_MAX]; + int port; + char* varptr; + char* s; + unsigned long v; + int rc; + + /* make a 1 port output to parse */ + snprintf(cline, sizeof(cline), "dump_counters -p 1"); + if (strlen(cline) + strlen(commandlinevars) > sizeof(cline)){ + msglog(LDMSD_LERROR, + SAMP ": Varname lengths will exceed MAX command line\n"); + return EINVAL; + } + strcat(cline, commandlinevars); + + /* Create a metric set of the required size */ + schema = base_schema_new(base); + if (!schema) { + msglog(LDMSD_LERROR, + "%s: The schema '%s' could not be created, errno=%d.\n", + __FILE__, base->schema_name, errno); + rc = EINVAL; + goto err1; + } + + rec_def = ldms_record_create("slingshot_port"); + if (!rec_def){ + rc = EINVAL; + goto err1; + } + + /* NOTE: need val 'x' (not 0 nor NULL) */ + msglog(LDMSD_LDEBUG, + SAMP ": Adding record %d '%s' type LDMS_V_CHAR_ARRAY \n", + 0, "port"); + rc = ldms_record_metric_add(rec_def, "port", "x", LDMS_V_U64, 1); + if (rc < 0){ + /* errno is already set */ + goto err3; + } + rec_metric_id_port = rc; + msglog(LDMSD_LDEBUG, + SAMP ": Port metric id '%d'\n", rec_metric_id_port); + msglog(LDMSD_LDEBUG, + SAMP ": will be executing '%s' to get the var names\n", + cline); + numvars = 0; + + mf = popen(cline, "r"); + if (!mf) { + msglog(LDMSD_LERROR, SAMP ": Could not execute '%s'\n", cline); + rc = ENOENT; + goto err3; + } + /* first line is just the header */ + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s){ + msglog(LDMSD_LERROR, SAMP ": Could not skip first line.\n"); + rc = EINVAL; + goto err3; + } + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s) + break; + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + + rc = sscanf(lbuf,"%d,%[^,],%lu", &port, varname, &v); + if (rc != 3) { + /* NOTE: Treating like this is the last line. */ + rc = 0; + break; + } + varptr = varname; + strip_whitespace(&varptr); + + msglog(LDMSD_LDEBUG, + SAMP ": Adding record %d '%s' type U64 \n", + numvars++, varptr); + /* NOTE: need val 'x' (not 0 nor NULL) */ + rc = ldms_record_metric_add(rec_def, varptr, "x", + LDMS_V_U64, 1); + if (rc < 0) { + /* errno is already set */ + goto err3; + } + msglog(LDMSD_LDEBUG, SAMP ": Added metric id '%d'\n", rc); + } while(s); + rc = 0; + + if (numvars == 0){ + msglog(LDMSD_LERROR, SAMP ": No vars!\n"); + rc = EINVAL; + goto err3; + } else { + msglog(LDMSD_LDEBUG, + SAMP ": Added %d records + port\n", numvars); + } + + rec_heap_sz = ldms_record_heap_size_get(rec_def); + heap_sz = SS_PORT_MAX * ldms_record_heap_size_get(rec_def); + + /* Add record definition into the schema */ + rec_def_idx = ldms_schema_record_add(schema, rec_def); + if (rec_def_idx < 0) { + rc = -rec_def_idx; + goto err3; + } + + /* Add a list (of records) */ + /* NOTE: if this is being allocated, can unnecessary ones be avoided? */ + ssport_list_mid = ldms_schema_metric_list_add(schema, "slingshot_port_list", + NULL, heap_sz); + if (ssport_list_mid < 0) { + rc = -ssport_list_mid; + goto err2; + } + + base_set_new(base); + if (!base->set) { + rc = errno; + goto err2; + } + + if (mf) + pclose(mf); + mf = NULL; + + return 0; +err3: + /* Only manually delete rec_def when it has not yet been added + to the schema */ + ldms_record_delete(rec_def); +err2: + base_schema_delete(base); + base = NULL; +err1: + if (mf) + pclose(mf); + mf = NULL; + + return rc; +} + + +/** + * check for invalid flags, with particular emphasis on warning the user about + */ +static int config_check(struct attr_value_list *kwl, + struct attr_value_list *avl, void *arg) +{ + char *value; + int i; + + char* deprecated[]={"set"}; + + for (i = 0; i < ARRAY_LEN(deprecated); i++){ + value = av_value(avl, deprecated[i]); + if (value){ + msglog(LDMSD_LERROR, + SAMP ": config argument %s has been deprecated.\n", + deprecated[i]); + return EINVAL; + } + } + + return 0; +} + +static const char *usage(struct ldmsd_plugin *self) +{ + return "config name=" SAMP " conffile=\n" \ + BASE_CONFIG_USAGE \ + " full path for the configuration file\n"; +} + +static int config(struct ldmsd_plugin *self, + struct attr_value_list *kwl, struct attr_value_list *avl) +{ + FILE* mf; + char* conffile; + char lbuf[SS_LINE_MAX]; + char varname[SS_VARNAME_MAX]; + char *s; + char *ivalue = NULL; + void *arg = NULL; + int rc; + + rc = config_check(kwl, avl, arg); + if (rc != 0){ + return rc; + } + + if (base) { + msglog(LDMSD_LERROR, SAMP ": Set already created.\n"); + return EINVAL; + } + + /* process conffile */ + ivalue = av_value(avl, "conffile"); + if (!ivalue) { + msglog(LDMSD_LERROR, + SAMP ": A configuration file is required.\n"); + goto err1; + } + + conffile = strdup(ivalue); + msglog(LDMSD_LDEBUG, + SAMP ": Should be trying to process conffile '%s'\n", conffile); + mf = fopen(conffile, "r"); + if (!mf) { + msglog(LDMSD_LERROR, SAMP ": Could not open '%s'\n", conffile); + rc = ENOENT; + goto err1; + } + + fseek(mf, 0, SEEK_SET); /* seek should work if get to EOF */ + /* first non-comment line MUST be "p=XXX" or "n=XXX" */ + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s){ + msglog(LDMSD_LERROR, + SAMP ": Bad format in file. First line noncomment line MUST be p=XXX or n=XXX\n"); + rc = EINVAL; + goto err1; + } + if ((strlen(lbuf) > 1) && (lbuf[0] == '#')){ + msglog(LDMSD_LDEBUG, + SAMP ": skipping input <%s>\n", lbuf); + continue; + } + + break; /* Got a line to parse */ + } while (s); + + /* note command line will be incomplete after this */ + rc = buildCommandLinePorts(lbuf); + if (rc) + goto err1; + + /* variables: store them in the slist, then execute + a command on a single port to get all the values. */ + strncpy(commandlinevars, "", sizeof(commandlinevars)); + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s) + break; + rc = sscanf(lbuf," %s", varname); /* NOTE: check for extra whitespace */ + if ((rc != 1) || (strlen(varname) == 0) || (varname[0] == '#')){ + msglog(LDMSD_LDEBUG, + SAMP ": skipping input <%s>\n", lbuf); + continue; + } + snprintf(lbuf, sizeof(lbuf), " -s %s", varname); + if (strlen(lbuf) + strlen(commandlinevars) >= + sizeof(commandlinevars)){ + msglog(LDMSD_LERROR, + SAMP ": varname lengths will exceed MAX command line\n"); + rc = EINVAL; + goto err1; + } + strcat(commandlinevars, lbuf); + } while(s); + + if (mf) + fclose(mf); + mf = NULL; + + base = base_config(avl, SAMP, SAMP, msglog); + if (!base){ + rc = EINVAL; + goto err; + } + + rc = create_metric_set(base); + if (rc) { + msglog(LDMSD_LERROR, SAMP ": failed to create a metric set.\n"); + goto err; + } + + snprintf(commandline, sizeof(commandline), "dump_counters"); + if (strlen(commandline) + strlen(commandlineports) + strlen(commandlinevars) > sizeof(commandline)){ + msglog(LDMSD_LERROR, + SAMP ": Ports and vars lengths will exceed MAX command line\n"); + rc = EINVAL; + goto err; + } + + strcat(commandline, commandlineports); + strcat(commandline, commandlinevars); + commandline_ok = 1; + + free(conffile); + conffile = NULL; + return 0; + + err: + base_del(base); + + err1: + if (mf) + pclose(mf); + mf = NULL; + free(conffile); + conffile = NULL; + numvars = 0; + numports = 0; + commandline[0] = '\0'; + commandline_ok = 0; + return rc; +} + +static int sample(struct ldmsd_sampler *self) +{ + FILE* mf; + char lbuf[SS_LINE_MAX]; + int port; + union ldms_value vport; + char varname[SS_VARNAME_MAX]; + union ldms_value* v; + char *s; + int i,j ; + ldms_mval_t lh, rec_inst; + size_t heap_sz; + int rc; + + + msglog(LDMSD_LDEBUG, SAMP ": in sample\n"); + + if (!base){ + msglog(LDMSD_LDEBUG, SAMP ": plugin not initialized\n"); + return EINVAL; + } + + mf = popen(commandline, "r"); + if (!mf) { + msglog(LDMSD_LERROR, + SAMP ": Could not execute '%s'\n", commandline); + return ENOENT; + } + + v = calloc(numvars, sizeof(union ldms_value)); + if (!v){ + msglog(LDMSD_LERROR, SAMP ": Out of memory.\n"); + if (mf) + pclose(mf); + mf = NULL; + return ENOMEM; + } + +begin: + base_sample_begin(base); + + lh = ldms_metric_get(base->set, ssport_list_mid); + + /* reset device data */ + ldms_list_purge(base->set, lh); + + fseek(mf, 0, SEEK_SET); //seek should work if get to EOF + + /* first line is just the header */ + s = fgets(lbuf, sizeof(lbuf), mf); + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + if (!s){ + msglog(LDMSD_LERROR, SAMP ": Bad format in command output.\n"); + rc = EINVAL; + goto ERR; + } + + /* for each port for each set of variables..... */ + i = 0; /* ports */ + j = 0; /* vars. NOTE: 'port' is not one of them */ + do { + s = fgets(lbuf, sizeof(lbuf), mf); + if (!s) + break; + msglog(LDMSD_LDEBUG, SAMP ": Read '%s'\n", lbuf); + + rc = sscanf(lbuf,"%d,%[^,],%lu", &port, varname, &v[j].v_u64); + if (rc != 3) { + msglog(LDMSD_LERROR, + SAMP ": Bad output to command on port %d variable %d (%d): '%s'\n", + i, j, rc, lbuf); + rc = EINVAL; + goto ERR; + } + if (j == (numvars-1)){ + rec_inst = ldms_record_alloc(base->set, rec_def_idx); + if (!rec_inst) + goto resize; + vport.v_u64 = port; + msglog(LDMSD_LDEBUG, + SAMP ": Should be setting port to %lu\n", + vport.v_u64); + ldms_record_set_u64(rec_inst, rec_metric_id_port, + vport.v_u64); + for (j = 0; j < numvars; j++){ + msglog(LDMSD_LDEBUG, + SAMP ": Should be setting var %d metric %d to %lu\n", + j, rec_metric_id_port+j+1, v[j].v_u64); + ldms_record_set_u64(rec_inst, + rec_metric_id_port+j+1, + v[j].v_u64); + } + ldms_list_append_record(base->set, lh, rec_inst); + j = 0; + i++; + } else { + j++; + } + if (i == numports){ + msglog(LDMSD_LDEBUG, + SAMP ": read everything expected. breaking\n"); + break; + /* NOTE: not checking if there are extra lines. + There will be one extra in the output */ + } + } while(s); + + base_sample_end(base); + if (mf) + pclose(mf); + mf = NULL; + + return 0; + +resize: + + free(v); + + /* + * We intend to leave the set in the inconsistent state so that + * the aggregators are aware that some metrics have not been newly + * sampled. + */ + heap_sz = ldms_set_heap_size_get(base->set) + 2*rec_heap_sz; + base_set_delete(base); + base_set_new_heap(base, heap_sz); + if (!base->set) { + rc = errno; + ldmsd_log(LDMSD_LCRITICAL, SAMP " : Failed to create a set with " + "a bigger heap. Error %d\n", rc); + return rc; + } + goto begin; + + ERR: + free(v); + base_sample_end(base); + if (mf) + pclose(mf); + mf = NULL; + + return rc; + +} + + +static void term(struct ldmsd_plugin *self) +{ + numvars = 0; + numports = 0; + commandline[0] = '\0'; + commandline_ok = 0; + base_set_delete(base); + base_del(base); + base = NULL; +} + + +static struct ldmsd_sampler slingshot_switch_1_plugin = { + .base = { + .name = SAMP, + .type = LDMSD_PLUGIN_SAMPLER, + .term = term, + .config = config, + .usage = usage, + }, + .get_set = get_set, + .sample = sample, +}; + +struct ldmsd_plugin *get_plugin(ldmsd_msg_log_f pf) +{ + msglog = pf; + base = NULL; + return &slingshot_switch_1_plugin.base; +}