diff options
Diffstat (limited to 'src/pminfo/pminfo.c')
-rw-r--r-- | src/pminfo/pminfo.c | 684 |
1 files changed, 684 insertions, 0 deletions
diff --git a/src/pminfo/pminfo.c b/src/pminfo/pminfo.c new file mode 100644 index 0000000..014c256 --- /dev/null +++ b/src/pminfo/pminfo.c @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2013-2014 Red Hat. + * Copyright (c) 1995-2001,2003 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include <limits.h> + +static void myeventdump(pmValueSet *, int, int); +static int myoverrides(int, pmOptions *); + +static pmLongOptions longopts[] = { + PMAPI_OPTIONS_HEADER("General options"), + PMOPT_ARCHIVE, + PMOPT_DEBUG, + PMOPT_HOST, + PMOPT_LOCALPMDA, + PMOPT_SPECLOCAL, + PMOPT_NAMESPACE, + PMOPT_DUPNAMES, + PMOPT_ORIGIN, + PMOPT_TIMEZONE, + PMOPT_HOSTZONE, + PMOPT_HELP, + PMAPI_OPTIONS_HEADER("Protocol options"), + { "batch", 1, 'b', "N", "fetch N metrics at a time for -f and -v [20]" }, + { "desc", 0, 'd', 0, "get and print metric description" }, + { "fetch", 0, 'f', 0, "fetch and print values for all instances" }, + { "fetchall", 0, 'F', 0, "fetch and print values for non-enumerable indoms" }, + { "pmid", 0, 'm', 0, "print PMID" }, + { "fullpmid", 0, 'M', 0, "print PMID in verbose format" }, + { "oneline", 0, 't', 0, "get and display (terse) oneline text" }, + { "helptext", 0, 'T', 0, "get and display (verbose) help text" }, + PMAPI_OPTIONS_HEADER("Metrics options"), + { "derived", 1, 'c', "FILE", "load derived metric definitions from FILE" }, + { "events", 0, 'x', 0, "unpack and report on any fetched event records" }, + { "verify", 0, 'v', 0, "verify mode, be quiet and only report errors" }, + PMAPI_OPTIONS_END +}; + +static pmOptions opts = { + .flags = PM_OPTFLAG_STDOUT_TZ, + .short_options = "a:b:c:dD:Ffh:K:LMmN:n:O:tTvxzZ:?", + .long_options = longopts, + .short_usage = "[options] [metricname ...]", + .override = myoverrides, +}; + +static int p_mid; /* Print metric IDs of leaf nodes */ +static int p_fullmid; /* Print verbose metric IDs of leaf nodes */ +static int p_desc; /* Print descriptions for metrics */ +static int p_oneline; /* fetch oneline text? */ +static int p_help; /* fetch help text? */ +static int p_value; /* pmFetch and print value(s)? */ +static int p_force; /* pmFetch and print value(s)? for non-enumerable indoms */ + +static int need_context; /* set if need a pmapi context */ +static int need_pmid; /* set if need to lookup names */ +static char **namelist; +static pmID *pmidlist; +static int batchsize = 20; +static int batchidx; +static int verify; /* Only print error messages */ +static int events; /* Decode event metrics */ +static pmID pmid_flags; +static pmID pmid_missed; + +/* + * pminfo has a few options which do not follow the defacto standards + */ +static int +myoverrides(int opt, pmOptions *opts) +{ + if (opt == 't' || opt == 'T') + return 1; /* we've claimed these, inform pmGetOptions */ + return 0; +} + +/* + * Cache all of the most recently requested pmInDom + */ +static char * +lookup(pmInDom indom, int inst) +{ + static pmInDom last = PM_INDOM_NULL; + static int numinst = -1; + static int *instlist; + static char **namelist; + int i; + + if (indom != last) { + if (numinst > 0) { + free(instlist); + free(namelist); + } + numinst = pmGetInDom(indom, &instlist, &namelist); + last = indom; + } + + for (i = 0; i < numinst; i++) { + if (instlist[i] == inst) + return namelist[i]; + } + + return NULL; +} + +/* + * we only ever have one metric + */ +static void +mydump(pmDesc *dp, pmValueSet *vsp, char *indent) +{ + int j; + char *p; + + if (indent != NULL) + printf("%s", indent); + if (vsp->numval == 0) { + printf("No value(s) available!\n"); + return; + } + else if (vsp->numval < 0) { + printf("Error: %s\n", pmErrStr(vsp->numval)); + return; + } + + for (j = 0; j < vsp->numval; j++) { + pmValue *vp = &vsp->vlist[j]; + if (dp->indom != PM_INDOM_NULL) { + if ((p = lookup(dp->indom, vp->inst)) == NULL) { + if (p_force) { + /* the instance disappeared; ignore it */ + printf(" inst [%d \"%s\"]\n", vp->inst, "DISAPPEARED"); + continue; + } + else { + /* report the error and give up */ + printf("pmNameIndom: indom=%s inst=%d: %s\n", + pmInDomStr(dp->indom), vp->inst, pmErrStr(PM_ERR_INST)); + printf(" inst [%d]", vp->inst); + } + } + else + printf(" inst [%d or \"%s\"]", vp->inst, p); + } + else + printf(" "); + printf(" value "); + pmPrintValue(stdout, vsp->valfmt, dp->type, vp, 1); + putchar('\n'); + if (!events) + continue; + if (dp->type == PM_TYPE_HIGHRES_EVENT) + myeventdump(vsp, j, 1); + else if (dp->type == PM_TYPE_EVENT) + myeventdump(vsp, j, 0); + } +} + +static void +setup_event_derived_metrics(void) +{ + if (pmid_flags == 0) { + /* + * get PMID for event.flags and event.missed + * note that pmUnpackEventRecords() will have called + * __pmRegisterAnon(), so the anonymous metrics + * should now be in the PMNS + */ + char *name_flags = "event.flags"; + char *name_missed = "event.missed"; + int sts; + + sts = pmLookupName(1, &name_flags, &pmid_flags); + if (sts < 0) { + /* should not happen! */ + fprintf(stderr, "Warning: cannot get PMID for %s: %s\n", + name_flags, pmErrStr(sts)); + /* avoid subsequent warnings ... */ + __pmid_int(&pmid_flags)->item = 1; + } + sts = pmLookupName(1, &name_missed, &pmid_missed); + if (sts < 0) { + /* should not happen! */ + fprintf(stderr, "Warning: cannot get PMID for %s: %s\n", + name_missed, pmErrStr(sts)); + /* avoid subsequent warnings ... */ + __pmid_int(&pmid_missed)->item = 1; + } + } +} + +static int +dump_nparams(int numpmid) +{ + if (numpmid == 0) { + printf(" ---\n"); + printf(" No parameters\n"); + return -1; + } + if (numpmid < 0) { + printf(" ---\n"); + printf(" Error: illegal number of parameters (%d)\n", numpmid); + return -1; + } + return 0; +} + +static void +dump_parameter(pmValueSet *xvsp, int index, int *flagsp) +{ + int sts, flags = *flagsp; + pmDesc desc; + char *name; + + if (pmNameID(xvsp->pmid, &name) >= 0) { + if (index == 0) { + if (xvsp->pmid == pmid_flags) { + flags = *flagsp = xvsp->vlist[0].value.lval; + printf(" flags 0x%x", flags); + printf(" (%s) ---\n", pmEventFlagsStr(flags)); + free(name); + return; + } + else + printf(" ---\n"); + } + if ((flags & PM_EVENT_FLAG_MISSED) && + (index == 1) && + (xvsp->pmid == pmid_missed)) { + printf(" ==> %d missed event records\n", + xvsp->vlist[0].value.lval); + free(name); + return; + } + printf(" %s (%s)\n", name, pmIDStr(xvsp->pmid)); + free(name); + } + else + printf(" PMID: %s\n", pmIDStr(xvsp->pmid)); + if ((sts = pmLookupDesc(xvsp->pmid, &desc)) < 0) + printf(" pmLookupDesc: %s\n", pmErrStr(sts)); + else + mydump(&desc, xvsp, " "); +} + +static void +myeventdump(pmValueSet *vsp, int inst, int highres) +{ + int r; /* event records */ + int p; /* event parameters */ + int nrecords; + int flags; + + if (highres) { + pmHighResResult **hr; + + if ((nrecords = pmUnpackHighResEventRecords(vsp, inst, &hr)) < 0) { + fprintf(stderr, "pmUnpackHighResEventRecords: %s\n", + pmErrStr(nrecords)); + return; + } + setup_event_derived_metrics(); + for (r = 0; r < nrecords; r++) { + printf(" --- event record [%d] timestamp ", r); + __pmPrintHighResStamp(stdout, &hr[r]->timestamp); + if (dump_nparams(hr[r]->numpmid) < 0) + continue; + flags = 0; + for (p = 0; p < hr[r]->numpmid; p++) + dump_parameter(hr[r]->vset[p], p, &flags); + } + pmFreeHighResEventResult(hr); + } + else { + pmResult **res; + + if ((nrecords = pmUnpackEventRecords(vsp, inst, &res)) < 0) { + fprintf(stderr, "pmUnpackEventRecords: %s\n", pmErrStr(nrecords)); + return; + } + setup_event_derived_metrics(); + for (r = 0; r < nrecords; r++) { + printf(" --- event record [%d] timestamp ", r); + __pmPrintStamp(stdout, &res[r]->timestamp); + if (dump_nparams(res[r]->numpmid) < 0) + continue; + flags = 0; + for (p = 0; p < res[r]->numpmid; p++) + dump_parameter(res[r]->vset[p], p, &flags); + } + pmFreeEventResult(res); + } +} + +static void +report(void) +{ + int i; + int sts; + pmDesc desc; + pmResult *result = NULL; + pmResult *xresult = NULL; + pmValueSet *vsp = NULL; + char *buffer; + int all_count; + int *all_inst; + char **all_names; + + if (batchidx == 0) + return; + + /* Lookup names. + * Cull out names that were unsuccessfully looked up. + * However, it is unlikely to fail because names come from a traverse PMNS. + */ + if (need_pmid) { + if ((sts = pmLookupName(batchidx, namelist, pmidlist)) < 0) { + int j = 0; + for (i = 0; i < batchidx; i++) { + if (pmidlist[i] == PM_ID_NULL) { + printf("%s: pmLookupName: %s\n", namelist[i], pmErrStr(sts)); + free(namelist[i]); + } + else { + /* assert(j <= i); */ + pmidlist[j] = pmidlist[i]; + namelist[j] = namelist[i]; + j++; + } + } + batchidx = j; + } + } + + if (p_value || verify) { + if (opts.context == PM_CONTEXT_ARCHIVE) { + if ((sts = pmSetMode(PM_MODE_FORW, &opts.origin, 0)) < 0) { + fprintf(stderr, "%s: pmSetMode failed: %s\n", pmProgname, pmErrStr(sts)); + exit(1); + } + } + if ((sts = pmFetch(batchidx, pmidlist, &result)) < 0) { + for (i = 0; i < batchidx; i++) + printf("%s: pmFetch: %s\n", namelist[i], pmErrStr(sts)); + goto done; + } + } + + for (i = 0; i < batchidx; i++) { + + if (p_desc || p_value || verify) { + if ((sts = pmLookupDesc(pmidlist[i], &desc)) < 0) { + printf("%s: pmLookupDesc: %s\n", namelist[i], pmErrStr(sts)); + continue; + } + } + + if (p_desc || p_help || p_value) + /* Not doing verify, output separator */ + putchar('\n'); + + + if (p_value || verify) { + vsp = result->vset[i]; + if (p_force) { + if (result->vset[i]->numval == PM_ERR_PROFILE) { + /* indom is non-enumerable; try harder */ + if ((all_count = pmGetInDom(desc.indom, &all_inst, &all_names)) > 0) { + pmDelProfile(desc.indom, 0, NULL); + pmAddProfile(desc.indom, all_count, all_inst); + if (xresult != NULL) { + pmFreeResult(xresult); + xresult = NULL; + } + if (opts.context == PM_CONTEXT_ARCHIVE) { + if ((sts = pmSetMode(PM_MODE_FORW, &opts.origin, 0)) < 0) { + fprintf(stderr, "%s: pmSetMode failed: %s\n", pmProgname, pmErrStr(sts)); + exit(1); + } + } + if ((sts = pmFetch(1, &pmidlist[i], &xresult)) < 0) { + printf("%s: pmFetch: %s\n", namelist[i], pmErrStr(sts)); + continue; + } + vsp = xresult->vset[0]; + /* leave the profile in the default state */ + free(all_inst); + free(all_names); + pmDelProfile(desc.indom, 0, NULL); + pmAddProfile(desc.indom, 0, NULL); + } + else if (all_count == 0) { + printf("%s: pmGetIndom: No instances?\n", namelist[i]); + continue; + } + else { + printf("%s: pmGetIndom: %s\n", namelist[i], pmErrStr(all_count)); + continue; + } + } + } + } + + if (verify) { + if (desc.type == PM_TYPE_NOSUPPORT) + printf("%s: Not Supported\n", namelist[i]); + else if (vsp->numval < 0) + printf("%s: %s\n", namelist[i], pmErrStr(vsp->numval)); + else if (vsp->numval == 0) + printf("%s: No value(s) available\n", namelist[i]); + continue; + } + else + /* not verify */ + printf("%s", namelist[i]); + + if (p_mid) + printf(" PMID: %s", pmIDStr(pmidlist[i])); + if (p_fullmid) + printf(" = %d = 0x%x", pmidlist[i], pmidlist[i]); + + if (p_oneline) { + if ((sts = pmLookupText(pmidlist[i], PM_TEXT_ONELINE, &buffer)) == 0) { + if (p_fullmid) + printf("\n "); + else + putchar(' '); + printf("[%s]", buffer); + free(buffer); + } + else + printf(" One-line Help: Error: %s\n", pmErrStr(sts)); + } + putchar('\n'); + + if (p_desc) + __pmPrintDesc(stdout, &desc); + + if (p_help) { + if ((sts = pmLookupText(pmidlist[i], PM_TEXT_HELP, &buffer)) == 0) { + char *p; + for (p = buffer; *p; p++) + ; + while (p > buffer && p[-1] == '\n') { + p--; + *p = '\0'; + } + if (*buffer != '\0') { + printf("Help:\n"); + printf("%s", buffer); + putchar('\n'); + } + else + printf("Help: <empty entry>\n"); + free(buffer); + } + else + printf("Full Help: Error: %s\n", pmErrStr(sts)); + } + + if (p_value) { + mydump(&desc, vsp, NULL); + } + } + + if (result != NULL) { + pmFreeResult(result); + result = NULL; + } + if (xresult != NULL) { + pmFreeResult(xresult); + xresult = NULL; + } + +done: + for (i = 0; i < batchidx; i++) + free(namelist[i]); + batchidx = 0; +} + +static void +dometric(const char *name) +{ + if (*name == '\0') { + printf("PMNS appears to be empty!\n"); + return; + } + + namelist[batchidx]= strdup(name); + if (namelist[batchidx] == NULL) { + fprintf(stderr, "%s: namelist string malloc: %s\n", pmProgname, osstrerror()); + exit(1); + } + + batchidx++; + if (batchidx >= batchsize) + report(); +} + +int +main(int argc, char **argv) +{ + int a, c; + int sts; + int exitsts = 0; + char *source; + char *endnum; + + while ((c = pmGetOptions(argc, argv, &opts)) != EOF) { + switch (c) { + case 'b': /* batchsize */ + batchsize = (int)strtol(opts.optarg, &endnum, 10); + if (*endnum != '\0') { + pmprintf("%s: -b requires numeric argument\n", pmProgname); + opts.errors++; + } + break; + + case 'c': /* derived metrics config file */ + sts = pmLoadDerivedConfig(opts.optarg); + if (sts < 0) { + fprintf(stderr, "%s: -c error: %s\n", pmProgname, pmErrStr(sts)); + /* errors are not necessarily fatal ... */ + } + break; + + case 'd': + p_desc = 1; + need_context = 1; + need_pmid = 1; + break; + + case 'F': + p_force = p_value = 1; + need_context = 1; + need_pmid = 1; + break; + + case 'f': + p_value = 1; + need_context = 1; + need_pmid = 1; + break; + + case 'M': + p_fullmid = 1; + p_mid = 1; + need_pmid = 1; + break; + + case 'm': + p_mid = 1; + need_pmid = 1; + break; + + case 't': + p_oneline = 1; + need_context = 1; + need_pmid = 1; + break; + + case 'T': + p_help = 1; + need_context = 1; + need_pmid = 1; + break; + + case 'v': + verify = 1; + need_context = 1; + need_pmid = 1; + break; + + case 'x': + events = p_value = 1; + need_context = 1; + need_pmid = 1; + break; + } + } + if (opts.errors) { + pmUsageMessage(&opts); + exit(1); + } + + if (opts.context) + need_context = 1; + + if (opts.context == PM_CONTEXT_ARCHIVE) + /* + * for archives, one metric per batch and start at beginning of + * archive for each batch so metric will be found if it is in + * the archive + */ + batchsize = 1; + + if (verify) + p_desc = p_mid = p_fullmid = p_help = p_oneline = p_value = p_force = 0; + + + + if ((namelist = (char **)malloc(batchsize * sizeof(char *))) == NULL) { + fprintf(stderr, "%s: namelist malloc: %s\n", pmProgname, osstrerror()); + exit(1); + } + + if ((pmidlist = (pmID *)malloc(batchsize * sizeof(pmID))) == NULL) { + fprintf(stderr, "%s: pmidlist malloc: %s\n", pmProgname, osstrerror()); + exit(1); + } + + if (!opts.nsflag) + need_context = 1; /* for distributed PMNS as no PMNS file given */ + + if (need_context) { + int ctxid; + + if (opts.context == PM_CONTEXT_ARCHIVE) + source = opts.archives[0]; + else if (opts.context == PM_CONTEXT_HOST) + source = opts.hosts[0]; + else if (opts.context == PM_CONTEXT_LOCAL) + source = NULL; + else { + opts.context = PM_CONTEXT_HOST; + source = "local:"; + } + if ((sts = pmNewContext(opts.context, source)) < 0) { + if (opts.context == PM_CONTEXT_HOST) + fprintf(stderr, "%s: Cannot connect to PMCD on host \"%s\": %s\n", + pmProgname, source, pmErrStr(sts)); + else if (opts.context == PM_CONTEXT_LOCAL) + fprintf(stderr, "%s: Cannot make standalone connection on localhost: %s\n", + pmProgname, pmErrStr(sts)); + else + fprintf(stderr, "%s: Cannot open archive \"%s\": %s\n", + pmProgname, source, pmErrStr(sts)); + exit(1); + } + ctxid = sts; + + if (opts.context == PM_CONTEXT_ARCHIVE) { + pmTrimNameSpace(); + /* complete TZ and time window option (origin) setup */ + if (pmGetContextOptions(ctxid, &opts)) { + pmflush(); + exit(1); + } + } + } + + if (opts.optind >= argc) { + sts = pmTraversePMNS("", dometric); + if (sts < 0) { + fprintf(stderr, "Error: %s\n", pmErrStr(sts)); + exitsts = 1; + } + } + else { + for (a = opts.optind; a < argc; a++) { + sts = pmTraversePMNS(argv[a], dometric); + if (sts < 0) { + fprintf(stderr, "Error: %s: %s\n", argv[a], pmErrStr(sts)); + exitsts = 1; + } + } + } + report(); + + exit(exitsts); +} |