Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1300 lines (1179 sloc) 32.5 KB
/*
* pmval - simple performance metrics value dumper
*
* Copyright (c) 2014-2015 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 "pmval.h"
static pmLongOptions longopts[] = {
PMAPI_GENERAL_OPTIONS,
PMOPT_SPECLOCAL,
PMOPT_LOCALPMDA,
PMOPT_CONTAINER,
PMOPT_DERIVED,
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" },
{ "verbose", 0, 'v', 0, "increase diagnostic output" },
{ "width", 1, 'w', "N", "set the width of each column of output" },
{ "filter", 1, 'x', "VALUE", "store to the metric before fetching (filter)" },
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:vw:x:",
.long_options = longopts,
.short_usage = "[options] metricname",
.override = override,
};
int verbose;
int archive;
static int pauseFlag;
static int rawEvents;
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;
static int reporting; /* set once reporting values starts */
/*
* 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 || rawEvents)
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 (archive)
e = pmLookupInDomArchive(x->desc.indom, *np);
else
e = pmLookupInDom(x->desc.indom, *np);
if (e < 0) {
fprintf(stderr, "%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 (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, pmMetricSpec *msp, int argc, char **argv)
{
int e;
x->metric = msp->metric;
if (msp->ninst > 0) {
x->inum = msp->ninst;
x->iall = (x->inum == 0);
x->inames = &msp->inst[0];
}
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.indom == PM_INDOM_NULL && msp->ninst > 0) {
fprintf(stderr, "%s: %s: singular metrics do not have instances\n",
pmProgname, msp->metric);
exit(EXIT_FAILURE);
}
if (x->desc.type == PM_TYPE_EVENT ||
x->desc.type == PM_TYPE_HIGHRES_EVENT) {
amode = PM_MODE_FORW; /* do no interpolate events */
rawArchive = archive;
rawEvents = 1;
}
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");
reporting = 0;
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) {
if (opts.guiflag) {
pmTimeStateBounds(&controls, pmtime);
return -1;
}
exit(EXIT_SUCCESS);
}
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 (__pmtimevalToReal(&r->timestamp) > __pmtimevalToReal(&opts.finish)) {
pmFreeResult(r);
return -2;
}
e = r->vset[i]->numval;
if (e == 0) {
if (opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) {
__pmPrintStamp(stdout, &r->timestamp);
printf(" ");
}
if (!rawEvents)
printf("No values available\n");
else if (rawEvents && verbose)
printf("%s: No values available\n", x->metric);
pmFreeResult(r);
return -1;
}
else if (e < 0) {
if (rawEvents && e == PM_ERR_NOTCONN) {
pmFreeResult(r);
exit(EXIT_SUCCESS);
}
else 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_HIGHRES_EVENT:
case PM_TYPE_EVENT: return 0;
case PM_TYPE_AGGREGATE_STATIC:
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;
if (!rawEvents)
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((const time_t *)&opts.origin.tv_sec, tbfr));
if (opts.finish.tv_sec != INT_MAX)
printf("end: %s", pmCtime((const time_t *)&opts.finish.tv_sec, tbfr));
}
if (rawEvents)
goto footer;
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');
footer:
/* 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 (rawEvents)
return;
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 sem, 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 (v < 0.0 && sem == PM_SEM_COUNTER)
printf("%*s", minwidth, "!");
else {
if (fixed != -1) {
printf("%*.*f", minwidth, fixed, v);
}
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) {
int sts;
sts = pmExtractValue(vset->valfmt, &vset->vlist[0], x->desc.type, &av, PM_TYPE_DOUBLE);
if (sts < 0) {
fprintf(stderr, "%s:printvals pmExtractValue: %s\n", pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
printreal(av.d, x->desc.sem, 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) {
int sts;
sts = pmExtractValue(vset->valfmt, &vset->vlist[j], x->desc.type, &av, PM_TYPE_DOUBLE);
if (sts < 0) {
fprintf(stderr, "%s:printvals[%d] pmExtractValue: %s\n", pmProgname, j, pmErrStr(sts));
exit(EXIT_FAILURE);
}
printreal(av.d, x->desc.sem, 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) {
int sts;
sts = pmExtractValue(vset->valfmt, &vset->vlist[j], x->desc.type, &av, PM_TYPE_DOUBLE);
if (sts < 0) {
fprintf(stderr, "%s:printvals[%d] pmExtractValue: %s\n", pmProgname, j, pmErrStr(sts));
exit(EXIT_FAILURE);
}
printreal(av.d, x->desc.sem, 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;
int sts;
sts = pmExtractValue(valfmt, val1, type, &a, PM_TYPE_DOUBLE);
if (sts < 0) {
fprintf(stderr, "%s:printrate prev pmExtractValue: %s\n", pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
sts = pmExtractValue(valfmt, val2, type, &b, PM_TYPE_DOUBLE);
if (sts < 0) {
fprintf(stderr, "%s:printrate this pmExtractValue: %s\n", pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
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, PM_SEM_COUNTER, 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, k;
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)
continue;
/* fast path - instance in same position in both valuelists */
k = j;
if (k < vset2->numval && vset1->vlist[j].inst == vset2->vlist[k].inst)
break;
/* scan for matching instance identifier, it may have moved */
for (k = 0; k < vset2->numval; k++)
if (vset1->vlist[j].inst == vset2->vlist[k].inst)
break;
break;
}
if ((j < vset1->numval) && (k < vset2->numval) &&
(vset1->vlist[j].inst == vset2->vlist[k].inst))
printrate(vset1->valfmt, x->desc.type, &vset1->vlist[j], &vset2->vlist[k], 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);
}
}
}
}
static void
printtime(struct timeval *tv)
{
char tbfr[26];
char *tp;
tp = pmCtime((const time_t *)&tv->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
*/
if (rawEvents)
fprintf(stderr, "[%24.24s]\n", tp);
else
fprintf(stderr, "[%8.8s]\n", &tp[11]);
}
/*
* 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 */
}
/*
* Server-side filtering - send the given filter value to pmcd
*/
static void
initfilters(Context *x, int conntype)
{
pmAtomValue atom;
pmValueSet *vset;
pmResult *result;
size_t length;
int count, type, sts, i ;
if (!x->filter || conntype != 0)
return;
count = x->inum ? x->inum : 1;
length = sizeof(pmValueSet) + sizeof(pmValue) * (count - 1);
if ((vset = (pmValueSet *)calloc(1, length)) == NULL)
__pmNoMem("store vset", length, PM_FATAL_ERR);
length = sizeof(pmResult);
if ((result = (pmResult *)calloc(1, length)) == NULL)
__pmNoMem("store result", length, PM_FATAL_ERR);
result->vset[0] = vset;
result->numpmid = 1;
if (rawEvents) {
type = PM_TYPE_STRING;
atom.cp = x->filter;
} else {
type = x->desc.type;
sts = __pmStringValue(x->filter, &atom, type);
if (sts == PM_ERR_TYPE) {
fprintf(stderr, "%s: filter \"%s\" incompatible with metric "
"type (PM_TYPE_%s)\n",
pmProgname, x->filter, pmTypeStr(type));
exit(EXIT_FAILURE);
}
if (sts == -ERANGE) {
fprintf(stderr, "%s: filter value \"%s\" is out of range for "
"metric data type (PM_TYPE_%s)\n",
pmProgname, x->filter, pmTypeStr(type));
exit(EXIT_FAILURE);
}
if (sts < 0) {
fprintf(stderr, "%s: failed to convert value \"%s\": %s\n",
pmProgname, x->filter, pmErrStr(sts));
exit(EXIT_FAILURE);
}
}
vset->pmid = x->pmid;
vset->numval = count;
for (i = 0; i < count; i++) {
if (x->inum)
vset->vlist[i].inst = x->iids[i];
else
vset->vlist[i].inst = PM_IN_NULL;
if ((sts = __pmStuffValue(&atom, &vset->vlist[i], type)) < 0) {
fprintf(stderr, "%s: stuff value \"%s\" failed: %s\n",
pmProgname, x->filter, pmErrStr(sts));
exit(EXIT_FAILURE);
}
vset->valfmt = sts;
}
if ((sts = pmStore(result)) < 0) {
fprintf(stderr, "%s: store value \"%s\" failed: %s\n",
pmProgname, x->filter, pmErrStr(sts));
exit(EXIT_FAILURE);
}
pmFreeResult(result);
}
int
main(int argc, char *argv[])
{
int c;
int ctx;
int sts;
int type;
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;
idx1 = context.inum;
subopt = getinstance(opts.optarg);
while (subopt != NULL) {
idx1++;
context.inames =
(char **)realloc(context.inames, idx1 * (sizeof (char *)));
if (context.inames == NULL)
__pmNoMem("pmval.ip", idx1 * sizeof(char *), PM_FATAL_ERR);
*(context.inames + idx1 - 1) = subopt;
subopt = getinstance(NULL);
}
context.inum = idx1;
break;
case 'r':
rawCounter = 1;
break;
case 'U': /* non-interpolated archive (undocumented) */
__pmAddOptArchive(&opts, opts.optarg);
amode = PM_MODE_FORW;
rawArchive = 1;
break;
case 'v':
verbose = 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;
case 'x':
context.filter = opts.optarg;
break;
default:
opts.errors++;
break;
}
}
if (opts.flags & PM_OPTFLAG_EXIT) {
pmUsageMessage(&opts);
exit(0);
}
if (!opts.errors && opts.optind >= argc) {
pmprintf("%s: error - no metricname specified\n", pmProgname);
opts.errors++;
}
else if (!opts.errors && opts.optind < argc - 1) {
pmprintf("%s: error - too many arguments\n", pmProgname);
opts.errors++;
}
if (opts.errors) {
pmUsageMessage(&opts);
exit(EXIT_FAILURE);
}
/* parse uniform metric spec */
if (opts.nhosts > 0) {
source = opts.hosts[0];
type = 0;
}
else if (opts.narchives > 0) {
source = opts.archives[0];
type = archive = 1;
}
else if (opts.Lflag) {
source = NULL;
type = 2;
}
else {
source = "local:";
type = 0;
}
if (pmParseMetricSpec(argv[opts.optind], type, source, &msp, &errmsg) < 0) {
pmprintf("%s", errmsg);
free(errmsg);
opts.errors++;
}
else if (msp->isarch == 1) {
if (opts.narchives == 0)
__pmAddOptArchive(&opts, msp->source);
type = archive = 1; /* may differ now */
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);
}
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);
}
if (opts.context == PM_CONTEXT_HOST)
__pmSetClientIdArgv(argc, argv);
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.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, msp, argc, argv);
initinsts(&context);
initfilters(&context, type);
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++;
}
if (pmDebugOptions.appl0) {
fprintf(stderr, "first=%.6f", start);
printtime(&opts.start);
fprintf(stderr, "posn=%.6f", origin);
printtime(&opts.origin);
fprintf(stderr, "last=%.6f", finish);
printtime(&opts.finish);
fprintf(stderr, "delta=%.6f samples=%d\n", delta, opts.samples);
}
}
if (opts.timezone)
tzlabel = opts.timezone;
else {
if (!opts.tzflag && !rawEvents)
printf("\n");
tzlabel = (char *)context.hostname;
}
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);
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 (rawEvents) {
if (opts.guiflag || opts.context == PM_CONTEXT_ARCHIVE) {
__pmPrintStamp(stdout, &rslt2->timestamp);
printf(" ");
}
printevents(&context, rslt2->vset[idx2], cols);
reporting = 1;
continue;
}
else 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);
reporting = 1;
continue;
}
else if (no_values || reporting) {
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 (rawEvents)
printf(" ");
}
if (rawEvents)
printevents(&context, rslt1->vset[idx1], cols);
else 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);
reporting = 1;
/*
* 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;
}