diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmval/pmval.c | |
download | pcp-debian/3.9.10.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmval/pmval.c')
-rw-r--r-- | src/pmval/pmval.c | 1141 |
1 files changed, 1141 insertions, 0 deletions
diff --git a/src/pmval/pmval.c b/src/pmval/pmval.c new file mode 100644 index 0000000..44dd8ba --- /dev/null +++ b/src/pmval/pmval.c @@ -0,0 +1,1141 @@ +/* + * pmval - simple performance metrics value dumper + * + * Copyright (c) 2014 Red Hat. + * Copyright (c) 2008-2009 Aconex. All Rights Reserved. + * Copyright (c) 1995-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 <math.h> +#include "pmapi.h" +#include "impl.h" +#include "pmtime.h" + +/* instance id - instance name association */ +typedef struct { + int id; + char *name; +} InstPair; + +/* full description of a performance metric */ +typedef struct { + /* external (printable) description */ + const char *hostname; + char *metric; /* name of metric */ + int iall; /* all instances */ + int inum; /* number of instances */ + char **inames; /* list of instance names */ + /* internal description */ + int handle; /* context handle */ + pmID pmid; /* metric identifier */ + pmDesc desc; /* metric description */ + float scale; /* conversion factor for rate */ + int *iids; /* list of instance ids */ + /* internal-external association */ + InstPair *ipairs; /* sorted array of id-name */ +} Context; + +static pmLongOptions longopts[] = { + PMAPI_GENERAL_OPTIONS, + PMOPT_SPECLOCAL, + PMOPT_LOCALPMDA, + PMAPI_OPTIONS_HEADER("Reporting options"), + { "delay", 0, 'd', 0, "delay, pause between updates for archive replay" }, + { "precision", 1, 'f', "N", "fixed output format with N digits precision" }, + { "instances", 1, 'i', "INST", "comma-separated metrics instance list" }, + { "raw", 0, 'r', 0, "output raw counter values (no rate conversion)" }, + { "nointerp", 1, 'U', "FILE", "non-interpolated fetching; ignores interval" }, + { "width", 1, 'w', "N", "set the width of each column of output" }, + PMAPI_OPTIONS_END +}; + +static int override(int, pmOptions *); +static pmOptions opts = { + .flags = PM_OPTFLAG_DONE | PM_OPTFLAG_BOUNDARIES | PM_OPTFLAG_STDOUT_TZ, + .short_options = PMAPI_OPTIONS "df:i:K:LrU:w:", + .long_options = longopts, + .short_usage = "[options] metricname", + .override = override, +}; + +static int pauseFlag; +static int rawCounter; +static int rawArchive; +static int fixed = -1; +static int nosamples; +static int cols; /* width of output column(s) */ +static int havePrev; /* have one sample, can compute rate */ +static int amode = PM_MODE_INTERP; /* archive scan mode */ +static char * source; +static char * tzlabel; +static pmTime * pmtime; +static pmTimeControls controls; +static Context context; + +/* + * Processing fetched values + */ + +/* Compare two InstPair's on their id fields. + - This function is passed as an argument to qsort, + hence the ugly casts. */ +static int /* -1 less, 0 equal, 1 greater */ +compare(const void *pair1, const void *pair2) +{ + if (((InstPair *)pair1)->id < ((InstPair *)pair2)->id) return -1; + if (((InstPair *)pair1)->id > ((InstPair *)pair2)->id) return 1; + return 0; +} + +/* Does the Context have names for all instances in the pmValueSet? */ +static int /* 1 yes, 0 no */ +chkinsts(Context *x, pmValueSet *vs) +{ + int i, j; + + if (x->desc.indom == PM_INDOM_NULL) + return 1; + + for (i = 0; i < vs->numval; i++) { + for (j = 0; j < x->inum; j++) { + if (vs->vlist[i].inst == x->ipairs[j].id) + break; + } + if (j == x->inum) + return 0; + } + return 1; +} + + +/* Fill in current instances into given Context. + Instances sorted by instance identifier. */ +static void +initinsts(Context *x) +{ + int *ip; + char **np; + InstPair *pp; + int n; + int e; + int i; + + if (x->desc.indom == PM_INDOM_NULL) + x->inum = 0; + else { + + /* fill in instance ids for given profile */ + if (! x->iall) { + n = x->inum; + np = x->inames; + ip = (int *)malloc(n * sizeof(int)); + if (ip == NULL) { + __pmNoMem("pmval.ip", n * sizeof(int), PM_FATAL_ERR); + } + x->iids = ip; + for (i = 0; i < n; i++) { + if (opts.context == PM_CONTEXT_ARCHIVE) + e = pmLookupInDomArchive(x->desc.indom, *np); + else + e = pmLookupInDom(x->desc.indom, *np); + if (e < 0) { + printf("%s: instance %s not available\n", pmProgname, *np); + exit(EXIT_FAILURE); + } + *ip = e; + np++; ip++; + } + ip = x->iids; + np = x->inames; + if ((e = pmAddProfile(x->desc.indom, x->inum, x->iids)) < 0) { + fprintf(stderr, "%s: pmAddProfile: %s\n", pmProgname, pmErrStr(e)); + exit(EXIT_FAILURE); + } + } + + /* find all available instances */ + else { + if (opts.context == PM_CONTEXT_ARCHIVE) + n = pmGetInDomArchive(x->desc.indom, &ip, &np); + else + n = pmGetInDom(x->desc.indom, &ip, &np); + if (n < 0) { + fprintf(stderr, "%s: pmGetInDom(%s): %s\n", pmProgname, pmInDomStr(x->desc.indom), pmErrStr(n)); + exit(EXIT_FAILURE); + } + x->inum = n; + x->iids = ip; + x->inames = np; + } + + /* build InstPair list and sort */ + pp = (InstPair *)malloc(n * sizeof(InstPair)); + if (pp == NULL) { + __pmNoMem("pmval.pp", n * sizeof(InstPair), PM_FATAL_ERR); + } + x->ipairs = pp; + for (i = 0; i < n; i++) { + pp->id = *ip; + pp->name = *np; + ip++; np++; pp++; + } + qsort(x->ipairs, (size_t)n, sizeof(InstPair), compare); + } +} + +/* Initialize API and fill in internal description for given Context. */ +static void +initapi(Context *x) +{ + int e; + + x->handle = pmWhichContext(); + + if ((e = pmLookupName(1, &(x->metric), &(x->pmid))) < 0) { + fprintf(stderr, "%s: pmLookupName(%s): %s\n", pmProgname, x->metric, pmErrStr(e)); + exit(EXIT_FAILURE); + } + + if ((e = pmLookupDesc(x->pmid, &(x->desc))) < 0) { + fprintf(stderr, "%s: pmLookupDesc: %s\n", pmProgname, pmErrStr(e)); + exit(EXIT_FAILURE); + } + + if (x->desc.sem == PM_SEM_COUNTER) { + if (x->desc.units.dimTime == 0) + x->scale = 1.0; + else { + if (x->desc.units.scaleTime > PM_TIME_SEC) + x->scale = pow(60, (PM_TIME_SEC - x->desc.units.scaleTime)); + else + x->scale = pow(1000, (PM_TIME_SEC - x->desc.units.scaleTime)); + } + } +} + +/* Fetch metric values. */ +static int +getvals(Context *x, /* in - full pm description */ + pmResult **vs) /* alloc - pm values */ +{ + pmResult *r; + int e; + int i; + + if (rawArchive) { + /* + * for -U mode, read until we find either a pmResult with the + * pmid we are after, or a mark record + */ + for ( ; ; ) { + e = pmFetchArchive(&r); + if (e < 0) + break; + + if (r->numpmid == 0) { + if (opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) + __pmPrintStamp(stdout, &r->timestamp); + printf(" Archive logging suspended\n"); + pmFreeResult(r); + return -1; + } + + for (i = 0; i < r->numpmid; i++) { + if (r->vset[i]->pmid == x->pmid) + break; + } + if (i != r->numpmid) + break; + pmFreeResult(r); + } + } + else { + e = pmFetch(1, &(x->pmid), &r); + i = 0; + } + + if (e < 0) { + if (e == PM_ERR_EOL && opts.guiflag) { + pmTimeStateBounds(&controls, pmtime); + return -1; + } + if (rawArchive) + fprintf(stderr, "\n%s: pmFetchArchive: %s\n", pmProgname, pmErrStr(e)); + else + fprintf(stderr, "\n%s: pmFetch: %s\n", pmProgname, pmErrStr(e)); + exit(EXIT_FAILURE); + } + + if (opts.guiflag) + pmTimeStateAck(&controls, pmtime); + + if ((double)r->timestamp.tv_sec + (double)r->timestamp.tv_usec/1000000 > + (double)opts.finish.tv_sec + (double)opts.finish.tv_usec/1000000) { + pmFreeResult(r); + return -2; + } + + if (r->vset[i]->numval == 0) { + if (opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) { + __pmPrintStamp(stdout, &r->timestamp); + printf(" "); + } + printf("No values available\n"); + pmFreeResult(r); + return -1; + } + else if (r->vset[i]->numval < 0) { + if (rawArchive) + fprintf(stderr, "\n%s: pmFetchArchive: %s\n", pmProgname, pmErrStr(r->vset[i]->numval)); + else + fprintf(stderr, "\n%s: pmFetch: %s\n", pmProgname, pmErrStr(r->vset[i]->numval)); + pmFreeResult(r); + return -1; + } + + *vs = r; + qsort(r->vset[i]->vlist, + (size_t)r->vset[i]->numval, + sizeof(pmValue), + compare); + + return i; +} + +static void +timestep(struct timeval delta) +{ + /* time moved, may need to wait for previous value again */ + havePrev = 0; +} + + +/* How many print positions required for value of given type? */ +static int +howide(int type) +{ + switch (type) { + case PM_TYPE_32: return 11; + case PM_TYPE_U32: return 11; + case PM_TYPE_64: return 21; + case PM_TYPE_U64: return 21; + case PM_TYPE_FLOAT: return 13; + case PM_TYPE_DOUBLE: return 21; + case PM_TYPE_STRING: return 21; + case PM_TYPE_AGGREGATE: return 21; + default: + fprintf(stderr, "pmval: unknown performance metric value type %s\n", pmTypeStr(type)); + exit(EXIT_FAILURE); + } +} + +/* Print parameter values as output header. */ +static void +printhdr(Context *x) +{ + pmUnits units; + char tbfr[26]; + const char *u; + + printf("metric: %s\n", x->metric); + + if (opts.context != PM_CONTEXT_ARCHIVE) + printf("host: %s\n", x->hostname); + else { + printf("archive: %s\n", source); + printf("host: %s\n", x->hostname); + printf("start: %s", pmCtime(&opts.origin.tv_sec, tbfr)); + if (opts.finish.tv_sec != INT_MAX) + printf("end: %s", pmCtime(&opts.finish.tv_sec, tbfr)); + } + + printf("semantics: "); + switch (x->desc.sem) { + case PM_SEM_COUNTER: + printf("cumulative counter"); + if (! rawCounter) printf(" (converting to rate)"); + break; + case PM_SEM_INSTANT: + printf("instantaneous value"); + break; + case PM_SEM_DISCRETE: + printf("discrete instantaneous value"); + break; + default: + printf("unknown"); + } + putchar('\n'); + + units = x->desc.units; + u = pmUnitsStr(&units); + printf("units: %s", *u == '\0' ? "none" : u); + if ((! rawCounter) && (x->desc.sem == PM_SEM_COUNTER)) { + printf(" (converting to "); + if (units.dimTime == 0) units.scaleTime = PM_TIME_SEC; + units.dimTime--; + if ((units.dimSpace == 0) && (units.dimTime == 0) && (units.dimCount == 0)) + printf("time utilization)"); + else { + u = pmUnitsStr(&units); + printf("%s)", *u == '\0' ? "none" : u); + } + } + putchar('\n'); + + /* sample count and interval */ + if (opts.samples < 0) printf("samples: all\n"); + else printf("samples: %d\n", opts.samples); + if ((opts.samples > 1) && + (opts.context != PM_CONTEXT_ARCHIVE || amode == PM_MODE_INTERP)) + printf("interval: %1.2f sec\n", __pmtimevalToReal(&opts.interval)); +} + +/* Print instance identifier names as column labels. */ +static void +printlabels(Context *x) +{ + int n = x->inum; + InstPair *pairs = x->ipairs; + int i; + static int style = -1; + + if (style == -1) { + InstPair *ip = pairs; + style = 0; + for (i = 0; i < n; i++) { + if (strlen(ip->name) > cols) { + style = 2; /* too wide */ + break; + } + if (strlen(ip->name) > cols-3) + style = 1; /* wide enough to change shift */ + ip++; + } + if (style == 2) { + ip = pairs; + for (i = 0; i < n; i++) { + printf("full label for instance[%d]: %s\n", i, ip->name); + ip++; + } + } + } + + putchar('\n'); + for (i = 0; i < n; i++) { + if ((opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) && i == 0) + printf(" "); + if (rawCounter || (x->desc.sem != PM_SEM_COUNTER) || style != 0) + printf("%*.*s ", cols, cols, pairs->name); + else { + if (fixed == -1) { + /* shift left by 3 places for decimal points in rate */ + printf("%*.*s ", cols-3, cols-3, pairs->name); + } + else { + /* no shift for fixed format */ + printf("%*.*s ", cols, cols, pairs->name); + } + } + pairs++; + } + if (n > 0) putchar('\n'); +} + +void +printreal(double v, int minwidth) +{ + char *fmt; + + /* + * <-- minwidth --> + * xxxxxxxxxxxxxxxxx + * ! no value + * x.xxxE-xx < 0.1 + * 0.0___ 0 + * x.xxxx 0.1 ... 0.9999 + * x.xxx_ 1 ... 9.999 + * xx.xx__ 10 ... 99.99 + * xxx.x___ 100 ... 999.9 + * xxxx.____ 1000 ... 9999 + * x.xxxE+xx > 9999 + */ + + if (fixed != -1) { + printf("%*.*f", minwidth, fixed, v); + } + else { + if (v < 0.0) + printf("%*s", minwidth, "!"); + else { + if (v == 0) { + fmt = "%*.0f.0 "; + minwidth -= 5; + } + else if (v < 0.1 || v > 9999) + fmt = "%*.3E"; + else if (v <= 0.9999) + fmt = "%*.4f"; + else if (v <= 9.999) { + fmt = "%*.3f "; + minwidth -= 1; + } + else if (v <= 99.99) { + fmt = "%*.2f "; + minwidth -= 2; + } + else if (v <= 999.9) { + fmt = "%*.1f "; + minwidth -= 3; + } + else { + fmt = "%*.0f. "; + minwidth -= 5; + } + printf(fmt, minwidth, v); + } + } +} + +/* Print performance metric values */ +static void +printvals(Context *x, pmValueSet *vset, int cols) +{ + int i, j; + pmAtomValue av; + int doreal = 0; + + if (x->desc.type == PM_TYPE_FLOAT || x->desc.type == PM_TYPE_DOUBLE) + doreal = 1; + + /* null instance domain */ + if (x->desc.indom == PM_INDOM_NULL) { + if (vset->numval == 1) { + if (doreal) { + pmExtractValue(vset->valfmt, &vset->vlist[0], x->desc.type, &av, PM_TYPE_DOUBLE); + printreal(av.d, cols); + } + else + pmPrintValue(stdout, vset->valfmt, x->desc.type, &vset->vlist[0], cols); + + } + else + printf("%*s", cols, "?"); + putchar('\n'); + } + + /* non-null instance domain */ + else { + for (i = 0; i < x->inum; i++) { + for (j = 0; j < vset->numval; j++) { + if (vset->vlist[j].inst == x->ipairs[i].id) + break; + } + if (j < vset->numval) { + if (doreal) { + pmExtractValue(vset->valfmt, &vset->vlist[j], x->desc.type, &av, PM_TYPE_DOUBLE); + printreal(av.d, cols); + } + else + pmPrintValue(stdout, vset->valfmt, x->desc.type, &vset->vlist[j], cols); + } + else + printf("%*s", cols, "?"); + putchar(' '); + } + putchar('\n'); + + for (j = 0; j < vset->numval; j++) { + for (i = 0; i < x->inum; i++) { + if (vset->vlist[j].inst == x->ipairs[i].id) + break; + } + if (x->iall == 1 && i == x->inum) { + printf("Warning: value="); + if (doreal) { + pmExtractValue(vset->valfmt, &vset->vlist[j], x->desc.type, &av, PM_TYPE_DOUBLE); + printreal(av.d, 1); + } + else + pmPrintValue(stdout, vset->valfmt, x->desc.type, &vset->vlist[j], 1); + printf(", but instance=%d is unknown\n", vset->vlist[j].inst); + } + } + } +} + +/* Print single performance metric rate value */ +static void +printrate(int valfmt, /* from pmValueSet */ + int type, /* from pmDesc */ + pmValue *val1, /* current value */ + pmValue *val2, /* previous value */ + double delta, /* time difference between samples */ + int minwidth) /* output is at least this wide */ +{ + pmAtomValue a, b; + double v; + static int dowrap = -1; + + pmExtractValue(valfmt, val1, type, &a, PM_TYPE_DOUBLE); + pmExtractValue(valfmt, val2, type, &b, PM_TYPE_DOUBLE); + v = a.d - b.d; + if (v < 0.0) { + if (dowrap == -1) { + /* PCP_COUNTER_WRAP in environment enables "counter wrap" logic */ + if (getenv("PCP_COUNTER_WRAP") == NULL) + dowrap = 0; + else + dowrap = 1; + } + if (dowrap) { + switch (type) { + case PM_TYPE_32: + case PM_TYPE_U32: + v += (double)UINT_MAX+1; + break; + case PM_TYPE_64: + case PM_TYPE_U64: + v += (double)ULONGLONG_MAX+1; + break; + } + } + } + v /= delta; + printreal(v, minwidth); +} + +/* Print performance metric rates */ +static void +printrates(Context *x, + pmValueSet *vset1, struct timeval stamp1, /* current values */ + pmValueSet *vset2, struct timeval stamp2, /* previous values */ + int cols) +{ + int i, j; + double delta; + + /* compute delta from timestamps and convert units */ + delta = x->scale * + (__pmtimevalToReal(&stamp1) - __pmtimevalToReal(&stamp2)); + + /* null instance domain */ + if (x->desc.indom == PM_INDOM_NULL) { + if ((vset1->numval == 1) && (vset2->numval == 1)) + printrate(vset1->valfmt, x->desc.type, &vset1->vlist[0], &vset2->vlist[0], delta, cols); + else + printf("%*s", cols, "?"); + putchar('\n'); + } + + /* non-null instance domain */ + else { + for (i = 0; i < x->inum; i++) { + for (j = 0; j < vset1->numval; j++) { + if (vset1->vlist[j].inst == x->ipairs[i].id) + break; + } + if ((j < vset1->numval) && (j < vset2->numval) && + (vset1->vlist[j].inst == vset2->vlist[j].inst)) + printrate(vset1->valfmt, x->desc.type, &vset1->vlist[j], &vset2->vlist[j], delta, cols); + else + printf("%*s", cols, "?"); + putchar(' '); + } + putchar('\n'); + + for (j = 0; j < vset1->numval; j++) { + for (i = 0; i < x->inum; i++) { + if (vset1->vlist[j].inst == x->ipairs[i].id) + break; + } + if (x->iall == 1 && i == x->inum && j < vset2->numval && + vset1->vlist[j].inst == vset2->vlist[j].inst) { + printf("Warning: value="); + printrate(vset1->valfmt, x->desc.type, &vset1->vlist[j], &vset2->vlist[j], delta, 1); + printf(", but instance=%d is unknown\n", vset1->vlist[j].inst); + } + } + } +} + + +/* + * like isspace, but for a string + */ +static int +hasspace(char *p) +{ + static const char *whitespace = ", \t\n"; + char *set = (char *)whitespace; + + if (p != NULL && *p) { + while (*set) { + if (*p == *set) + return 1; + set++; + } + } + return 0; +} + +/* + * like strtok, but smarter + */ +static char * +getinstance(char *p) +{ + static char *save; + char quot; + char *q; + char *start; + + if (p == NULL) + q = save; + else + q = p; + + while (hasspace(q)) + q++; + + if (*q == '\0') + return NULL; + else if (*q == '"' || *q == '\'') { + quot = *q; + start = ++q; + + while (*q && *q != quot) + q++; + if (*q == quot) + *q++ = '\0'; + } + else { + start = q; + while (*q && !hasspace(q)) + q++; + } + if (*q) + *q++ = '\0'; + save = q; + + return start; +} + +static int +override(int opt, pmOptions *opts) +{ + /* need to distinguish between zero argument or not requested */ + if (opt == 's') { + if (atoi(opts->optarg) == 0) + nosamples = 1; + } + return 0; /* continue on with using the common code, always */ +} + +int +main(int argc, char *argv[]) +{ + int c; + int i; + int ctx; + int sts; + int idx1; + int idx2 = 0; /* initialize to pander to gcc */ + int forever; + int no_values = 0; + char *subopt; + char *endnum; + char *errmsg; + pmMetricSpec *msp; + pmResult *rslt1; /* current values */ + pmResult *rslt2; /* previous values */ + + setlinebuf(stdout); + context.iall = 1; + + while ((c = pmGetOptions(argc, argv, &opts)) != EOF) { + switch (c) { + + case 'd': + pauseFlag = 1; + break; + + case 'f': /* fixed format count */ + fixed = (int)strtol(opts.optarg, &endnum, 10); + if (*endnum != '\0' || fixed < 0) { + pmprintf("%s: -f requires +ve numeric argument\n", pmProgname); + opts.errors++; + } + break; + + case 'i': /* instance names */ + context.iall = 0; + i = context.inum; + subopt = getinstance(opts.optarg); + while (subopt != NULL) { + i++; + context.inames = + (char **)realloc(context.inames, i * (sizeof (char *))); + if (context.inames == NULL) + __pmNoMem("pmval.ip", i * sizeof(char *), PM_FATAL_ERR); + *(context.inames + i - 1) = subopt; + subopt = getinstance(NULL); + } + context.inum = i; + break; + + case 'r': + rawCounter = 1; + break; + + case 'U': /* non-interpolated archive (undocumented) */ + __pmAddOptArchive(&opts, opts.optarg); + amode = PM_MODE_FORW; + rawArchive = 1; + break; + + case 'w': /* output column width */ + cols = (int)strtol(opts.optarg, &endnum, 10); + if (*endnum != '\0' || cols < 1) { + pmprintf("%s: -w requires +ve numeric argument\n", pmProgname); + opts.errors++; + } + break; + + default: + opts.errors++; + break; + } + } + + if (!opts.errors && opts.optind >= argc) { + pmprintf("Error: no metricname specified\n\n"); + opts.errors++; + } + else if (!opts.errors && opts.optind < argc - 1) { + pmprintf("Error: pmval can only process one metricname at a time\n\n"); + opts.errors++; + } + + if (opts.errors) { + pmUsageMessage(&opts); + exit(EXIT_FAILURE); + } + + /* parse uniform metric spec */ + if (opts.nhosts > 0) { + source = opts.hosts[0]; + i = 0; + } + else if (opts.narchives > 0) { + source = opts.archives[0]; + i = 1; + } + else if (opts.Lflag) { + source = NULL; + i = 2; + } + else { + source = "local:"; + i = 0; + } + if (pmParseMetricSpec(argv[opts.optind], i, source, &msp, &errmsg) < 0) { + pmprintf("%s", errmsg); + free(errmsg); + opts.errors++; + } + else if (msp->isarch == 1) { + if (opts.narchives == 0) + __pmAddOptArchive(&opts, msp->source); + source = msp->source; + } + else if (msp->isarch == 2) { + opts.Lflag = 1; + source = NULL; + } + else { + if (opts.nhosts == 0) + __pmAddOptHost(&opts, msp->source); + source = msp->source; + } + + /* + * As a result of allowing either getopts or the metricspec to specify + * the source of the metric, we delay option end processing until now. + */ + opts.flags &= ~PM_OPTFLAG_DONE; + __pmEndOptions(&opts); + + if (opts.context != PM_CONTEXT_ARCHIVE) { + if (rawArchive) { + pmprintf("%s: uninterpolated mode can only be used with archives", + pmProgname); + opts.errors++; + } + if (pauseFlag) { + pmprintf("%s: delay can only be used with archives\n", pmProgname); + opts.errors++; + } + } + else { + if (opts.guiflag && pauseFlag) { + pmprintf("%s: guiflag cannot be used with delay\n", pmProgname); + opts.errors++; + } + } + + if (opts.errors) { + pmUsageMessage(&opts); + exit(EXIT_FAILURE); + } + + context.metric = msp->metric; + if (msp->ninst > 0) { + context.inum = msp->ninst; + context.iall = (context.inum == 0); + context.inames = &msp->inst[0]; + } + + if ((sts = ctx = pmNewContext(opts.context, source)) < 0) { + if (opts.context == PM_CONTEXT_ARCHIVE) + fprintf(stderr, "%s: Cannot open archive \"%s\": %s\n", + pmProgname, source, pmErrStr(sts)); + else if (opts.context == PM_CONTEXT_HOST) + fprintf(stderr, "%s: Cannot connect to PMCD on host \"%s\": %s\n", + pmProgname, source, pmErrStr(sts)); + else + fprintf(stderr, "%s: Cannot establish local context: %s\n", + pmProgname, pmErrStr(sts)); + exit(EXIT_FAILURE); + } + + context.hostname = pmGetContextHostName(ctx); + if (strlen(context.hostname) == 0) { + fprintf(stderr, "%s: Cannot evaluate context host name: %s\n", + pmProgname, pmErrStr(sts)); + exit(EXIT_FAILURE); + } + + if (pmGetContextOptions(ctx, &opts) < 0) { + pmflush(); /* runtime errors only at this stage */ + exit(EXIT_FAILURE); + } + if (opts.timezone) + tzlabel = opts.timezone; + else { + if (!opts.tzflag) + printf("\n"); + tzlabel = (char *)context.hostname; + } + + if (!opts.samples && !nosamples) + opts.samples = -1; + if (!opts.guiport) + opts.guiport = -1; + if (!opts.finish.tv_sec) + opts.finish.tv_sec = INT_MAX; + if (opts.interval.tv_sec == 0 && opts.interval.tv_usec == 0) + opts.interval.tv_sec = 1; + + initapi(&context); + initinsts(&context); + + if (!(opts.guiflag || opts.guiport != -1) && + opts.samples < 0 && + opts.finish.tv_sec != INT_MAX && + amode != PM_MODE_FORW) { + double start, finish, origin, delta; + + start = __pmtimevalToReal(&opts.start); + finish = __pmtimevalToReal(&opts.finish); + origin = __pmtimevalToReal(&opts.origin); + delta = __pmtimevalToReal(&opts.interval); + + opts.samples = (int) ((finish - origin) / delta); + if (opts.samples < 0) + opts.samples = 0; /* if end is before start, no samples thanks */ + else { + /* + * p stands for posn + * + p + p+delta + p+2*delta + p+3*delta + last + * | | | | | | + * +-----------+-----------+-----------+-- ...... ----+---+---> time + * 1 2 3 smpls + * + * So we will perform smpls+1 fetches ... the number of reported + * values cannot be determined as it is usually (but not always + * thanks to interpolation mode in archives) one less for + * PM_SEM_COUNTER metrics. + * + * samples: as reported in the header output is the number + * of fetches to be attempted. + */ + opts.samples++; + } +#ifdef PCP_DEBUG + if (pmDebug & DBG_TRACE_APPL0) { + char tbfr[26]; + char *tp; + fprintf(stderr, "getargs: first=%.6f", start); + tp = pmCtime(&opts.start.tv_sec, tbfr); + /* + * tp -> Ddd Mmm DD HH:MM:SS YYYY\n + * 0 4 8 1 1 2 2 2 + * 1 8 0 3 4 + */ + fprintf(stderr, "[%8.8s]\n", &tp[11]); + fprintf(stderr, "getargs: posn=%.6f", origin); + tp = pmCtime(&opts.origin.tv_sec, tbfr); + fprintf(stderr, "[%8.8s]\n", &tp[11]); + fprintf(stderr, "getargs: last=%.6f", finish); + tp = pmCtime(&opts.finish.tv_sec, tbfr); + fprintf(stderr, "[%8.8s]\n", &tp[11]); + fprintf(stderr, "getargs: delta=%.6f samples=%d\n", + delta, opts.samples); + } +#endif + } + + if (opts.guiflag || opts.guiport != -1) { + /* set up pmtime control */ + pmWhichZone(&opts.timezone); + pmtime = pmTimeStateSetup(&controls, opts.context, opts.guiport, + opts.interval, opts.origin, opts.start, + opts.finish, opts.timezone, tzlabel); + controls.stepped = timestep; + opts.guiflag = 1; /* we're using pmtime control from here on */ + } + else if (opts.context == PM_CONTEXT_ARCHIVE) /* no time control, go it alone */ + pmTimeStateMode(amode, opts.interval, &opts.origin); + + /* TODO: bring logic from pmevent, reunify the two binaries */ + if (context.desc.type == PM_TYPE_EVENT || + context.desc.type == PM_TYPE_HIGHRES_EVENT) { + fprintf(stderr, "%s: Cannot display values for event type metrics\n", + pmProgname); + exit(EXIT_FAILURE); + } + + forever = (opts.samples < 0 || opts.guiflag); + + if (cols <= 0) + cols = howide(context.desc.type); + + if ((fixed == 0 && fixed > cols) || (fixed > 0 && fixed > cols - 2)) { + fprintf(stderr, "%s: -f %d too large for column width %d\n", + pmProgname, fixed, cols); + exit(EXIT_FAILURE); + } + + printhdr(&context); + + /* wait till time for first sample */ + if (opts.context != PM_CONTEXT_ARCHIVE) + __pmtimevalPause(opts.start); + + /* main loop fetching and printing sample values */ + while (forever || (opts.samples-- > 0)) { + if (opts.guiflag) + pmTimeStateVector(&controls, pmtime); + if (havePrev == 0) { + /* + * We don't yet have a value at the previous time point ... + * save this value so we can use it to compute the rate if + * the metric has counter semantics and we're doing rate + * conversion. + */ + if ((idx2 = getvals(&context, &rslt2)) >= 0) { + /* previous value success */ + havePrev = 1; + if (context.desc.indom != PM_INDOM_NULL) + printlabels(&context); + if (rawCounter || (context.desc.sem != PM_SEM_COUNTER)) { + /* not doing rate conversion, report this value immediately */ + if (opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) + __pmPrintStamp(stdout, &rslt2->timestamp); + printvals(&context, rslt2->vset[idx2], cols); + continue; + } + else if (no_values) { + if (opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) { + __pmPrintStamp(stdout, &rslt2->timestamp); + printf(" "); + } + printf("No values available\n"); + } + no_values = 0; + if (opts.guiflag) + /* pmtime controls timing */ + continue; + } + else if (idx2 == -2) + /* out the end of the window */ + break; + else + no_values = 1; + } + + /* wait till time for sample */ + if (!opts.guiflag && (pauseFlag || opts.context != PM_CONTEXT_ARCHIVE)) + __pmtimevalSleep(opts.interval); + + if (havePrev == 0) + continue; /* keep trying to get the previous sample */ + + /* next sample */ + if ((idx1 = getvals(&context, &rslt1)) == -2) + /* out the end of the window */ + break; + else if (idx1 < 0) { + /* + * Fall back to trying to get an initial sample because + * although we got the previous sample, we failed to get the + * next sample. + */ + havePrev = 0; + continue; + } + + /* refresh instance names */ + if (context.iall && ! chkinsts(&context, rslt1->vset[idx1])) { + free(context.iids); + if (context.iall) + free(context.inames); + free(context.ipairs); + initinsts(&context); + printlabels(&context); + } + + /* print values */ + if (opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) + __pmPrintStamp(stdout, &rslt1->timestamp); + if (rawCounter || (context.desc.sem != PM_SEM_COUNTER)) + printvals(&context, rslt1->vset[idx1], cols); + else + printrates(&context, rslt1->vset[idx1], rslt1->timestamp, + rslt2->vset[idx2], rslt2->timestamp, cols); + + /* + * discard previous and save current result, so this value + * becomes the previous value at the next iteration + */ + pmFreeResult(rslt2); + rslt2 = rslt1; + idx2 = idx1; + } + + /* + * All serious error conditions have explicit exit() calls, so + * if we get this far, all has gone well. + */ + return 0; +} |