1
0
Files
irix-657m-src/eoe/cmd/pcp/pmval/pmval.c
2022-09-29 17:59:04 +03:00

1611 lines
40 KiB
C

/***************************************************************************
* pmval - performance metrics value dumper
***************************************************************************
*
* Copyright 1995, Silicon Graphics, Inc.
* ALL RIGHTS RESERVED
*
* UNPUBLISHED -- Rights reserved under the copyright laws of the United
* States. Use of a copyright notice is precautionary only and does not
* imply publication or disclosure.
*
* U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights
* in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or
* in similar or successor clauses in the FAR, or the DOD or NASA FAR
* Supplement. Contractor/manufacturer is Silicon Graphics, Inc.,
* 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311.
*
* THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY
* INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION,
* DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY
* PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON
* GRAPHICS, INC.
*/
#ident "$Id: pmval.c,v 2.70 1999/05/11 00:28:03 kenmcd Exp $"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h> /* getopt, malloc, qsort and friends */
#include <sys/time.h> /* timeval and friends */
#include <limits.h> /* CLK_TCK */
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h> /* pow */
#include "pmapi.h"
#include "impl.h"
#include "pmapi_dev.h"
#if defined(IRIX6_5)
#include <optional_sym.h>
#endif
/***************************************************************************
* constants
***************************************************************************/
#define FALSE 0
#define TRUE 1
#define START -1
#define STANDBY 0
#define FORW 1
#define BACK 2
#define MOVING 3
#define ENDLOG 4
static char usage[] =
"Usage: %s [options] metricname\n\n"
"Options:\n"
" -A align align sample times on natural boundaries\n"
" -a archive metrics source is a PCP log archive\n"
" -d delay, pause between updates for archive replay\n"
" -g start in GUI mode with new time control\n"
" -h host metrics source is PMCD on host\n"
" -i instance metric instance or list of instances - elements in an\n"
" instance list are separated by commas or whitespace\n"
" -n pmnsfile use an alternative PMNS\n"
" -O offset initial offset into the time window\n"
" -p port port name for connection to existing time control\n"
" -r output raw counter values\n"
" -S starttime start of the time window\n"
" -s samples terminate after this many samples\n"
" -T endtime end of the time window\n"
" -t interval sample interval [default 1 second]\n"
" -w width set the width of each column of output\n"
" -Z timezone set reporting timezone\n"
" -z set reporting timezone to local time of metrics source\n";
/***************************************************************************
* type definitions
***************************************************************************/
/* instance id - instance name association */
typedef struct {
int id;
char *name;
} InstPair;
/* full description of a performance metric */
typedef struct {
/* external (printable) description */
char *host; /* name of host */
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;
/***************************************************************************
* Globals
***************************************************************************/
static char *archive = NULL;
static pmLogLabel label;
static char *pmnsfile = PM_NS_DEFAULT;
static char *rpt_tz = NULL;
static char *rpt_tz_label = NULL;
static int pauseFlag = 0;
static int raw = 0;
static int ahtype = PM_CONTEXT_HOST; /* archive or host? */
static int amode = PM_MODE_INTERP; /* archive scan mode */
static char local[] = "localhost";
static int gui = 0;
static int state = START;
static char *control_port = NULL;
static int control_fd;
static int lastdeltaunits; /* from -t or pmtime.vcrmode */
static int lastdelta; /* from -t or pmtime.delta */
static int rawarchive = 0;
static pmTime pmtime;
static struct timeval last = {INT_MAX, 999999}; /* end time for log */
/***************************************************************************
* timing functions
***************************************************************************/
/* add timevals */
static struct timeval
tadd(struct timeval t1, struct timeval t2)
{
t1.tv_sec += t2.tv_sec;
t1.tv_usec += t2.tv_usec;
if (t1.tv_usec > 1000000) {
(t1.tv_sec)++;
t1.tv_usec -= 1000000;
}
return t1;
}
/* subtract timevals */
static struct timeval
tsub(struct timeval t1, struct timeval t2)
{
t1.tv_usec -= t2.tv_usec;
if (t1.tv_usec < 0) {
t1.tv_usec += 1000000;
t1.tv_sec--;
}
t1.tv_sec -= t2.tv_sec;
return t1;
}
/* first timeval has reached second timeval to within 1 tick */
static int
reached(struct timeval t1, struct timeval t2)
{
static struct timeval tick = { 0, 0 };
if (tick.tv_usec == 0)
/* one trip, usec per tick */
tick.tv_usec = 1000000 / CLK_TCK;
t1 = tadd(t1, tick);
return (t1.tv_sec > t2.tv_sec) ||
(t1.tv_sec == t2.tv_sec && t1.tv_usec >= t2.tv_usec);
}
/* convert timeval to ticks
- positive time only
- accurate to 1 tick */
static long
toticks(struct timeval t)
{
static int ticks_per_sec = 0;
long ticks;
if (ticks_per_sec == 0)
ticks_per_sec = CLK_TCK;
ticks = ticks_per_sec * t.tv_sec + ticks_per_sec * t.tv_usec/1000000;
if (ticks > 0)
return ticks;
else
return 1L;
}
/* convert timeval to seconds */
static double
tosec(struct timeval t)
{
return t.tv_sec + (t.tv_usec / 1000000.0);
}
/* sleep until given timeval */
static void
sleeptill(struct timeval sched)
{
struct timeval curr; /* current time */
struct timeval delay; /* interval to sleep */
for (;;) { /* loop to catch early wakeup by sginap */
gettimeofday(&curr, NULL);
if (reached(curr,sched)) return;
delay = tsub(sched, curr);
sginap(toticks(delay));
}
}
/***************************************************************************
* 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 */
compair(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;
}
/***************************************************************************
* interface to performance metrics API
***************************************************************************/
/* 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);
/*NOTREACHED*/
}
x->iids = ip;
for (i = 0; i < n; i++) {
if (ahtype == 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 (ahtype == 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\n", pmProgname, 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);
/*NOTREACHED*/
}
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), compair);
}
}
/* Initialize API and fill in internal description for given Context. */
static void
initapi(Context *x)
{
int e;
x->handle = pmWhichContext();
if (pmnsfile != PM_NS_DEFAULT) {
if ((e = pmLoadNameSpace(pmnsfile)) < 0) {
fprintf(stderr, "%s: pmLoadNameSpace: %s\n", pmProgname, pmErrStr(e));
exit(EXIT_FAILURE);
}
}
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));
}
}
}
static void
ack_tctl(struct timeval *now)
{
int sts;
/* let pmtime control know we are done */
if ((sts = pmTimeSendAck(now)) < 0) {
if (sts == -EPIPE)
fprintf(stderr, "\n%s: Time Controller has exited, goodbye\n",
pmProgname);
else
fprintf(stderr, "\n%s: pmTimeSendAck: %s\n", pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
}
/* 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 (gui || archive != NULL)
__pmPrintStamp(stdout, &r->timestamp);
printf(" Archive logging suspended\n");
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 && gui) {
ack_tctl(&last);
if (state != ENDLOG) {
printf("\n[Time Control] End of Archive ...\n");
state = ENDLOG;
}
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 (gui)
ack_tctl(&r->timestamp);
if ((double)r->timestamp.tv_sec + (double)r->timestamp.tv_usec/1000000 >
(double)last.tv_sec + (double)last.tv_usec/1000000) {
return -2;
}
if (r->vset[i]->numval == 0) {
if (gui || archive != NULL) {
__pmPrintStamp(stdout, &r->timestamp);
printf(" ");
}
printf("No values available\n");
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));
return -1;
}
*vs = r;
qsort(&r->vset[i]->vlist[i],
(size_t)r->vset[i]->numval,
sizeof(pmValue),
compair);
return i;
}
/***************************************************************************
* output
***************************************************************************/
/* 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\n");
exit(EXIT_FAILURE);
}
/*NOTREACHED*/
}
/*
* Get Extended Time Base interval and Units from a timeval
*/
#define SECS_IN_24_DAYS 2073600.0
static void
getXTBintervalFromTimeval(int *ival, int *mode, struct timeval *tval)
{
double tmp_ival = tval->tv_sec + tval->tv_usec / 1000000.0;
if (tmp_ival > SECS_IN_24_DAYS) {
*ival = (int)tmp_ival;
*mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_SEC);
}
else {
*ival = (int)(tmp_ival * 1000.0);
*mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_MSEC);
}
}
/*
* Get the interval in seconds
*/
static double
getXTBinSeconds(int *ival, int *mode)
{
double rval = 0.0;
switch(PM_XTB_GET(*mode)) {
case PM_TIME_NSEC:
rval = *ival / 1000000000.0;
break;
case PM_TIME_USEC:
rval = *ival / 1000000.0;
break;
case PM_TIME_SEC:
rval = (double)*ival;
break;
case PM_TIME_MIN:
rval = *ival * 60;
break;
case PM_TIME_HOUR:
rval = *ival * 3600;
break;
case PM_TIME_MSEC:
default:
/* default (not XTB) is milliseconds */
rval = *ival / 1000.0;
break;
}
return rval;
}
/* Print parameter values as output header. */
static void
printhdr(Context *x, long smpls, struct timeval delta, struct timeval first)
{
pmUnits units;
time_t t;
char tbfr[26];
const char *u;
/* metric name */
printf("metric: %s\n", x->metric);
/* live host */
if (archive == NULL)
printf("host: %s\n", x->host);
/* archive */
else {
printf("archive: %s\n", archive);
printf("host: %s\n", label.ll_hostname);
t = (time_t) first.tv_sec;
printf("start: %s", pmCtime(&t, tbfr));
if (last.tv_sec != INT_MAX)
printf("end: %s", pmCtime(&last.tv_sec, tbfr));
}
/* semantics */
printf("semantics: ");
switch (x->desc.sem) {
case PM_SEM_COUNTER:
printf("cumulative counter");
if (! raw) 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 */
units = x->desc.units;
u = pmUnitsStr(&units);
printf("units: %s", *u == '\0' ? "none" : u);
if ((! raw) && (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 */
if (smpls == 0) printf("samples: all\n");
else printf("samples: %i\n", smpls);
if (smpls != 1 && (ahtype != PM_CONTEXT_ARCHIVE || amode == PM_MODE_INTERP)) {
printf("interval: %1.2f sec\n", tosec(delta));
getXTBintervalFromTimeval(&lastdelta, &lastdeltaunits, &delta);
}
}
/* Print instance identifier names as column labels. */
static void
printlabels(Context *x, int cols)
{
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 ((gui || archive != NULL) && i == 0)
printf(" ");
if (raw || (x->desc.sem != PM_SEM_COUNTER) || style != 0)
printf("%*.*s ", cols, cols, pairs->name);
else
/* shift left by 3 places for decimal points in rate */
printf("%*.*s ", cols-3, cols-3, 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 (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 * (tosec(stamp1) - tosec(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);
}
}
}
}
/***************************************************************************
* command line processing
***************************************************************************/
#define WHITESPACE ", \t\n"
static int
isany(char *p, char *set)
{
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 (isany(q, WHITESPACE))
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 && !isany(q, WHITESPACE))
q++;
}
if (*q)
*q++ = '\0';
save = q;
return start;
}
/* extract command line arguments - exits on error */
static void
getargs(int argc, /* in - command line argument count */
char *argv[], /* in - argument strings */
Context *cntxt, /* out - full pm description */
struct timeval *posn, /* out - first sample time */
struct timeval *delta, /* out - sample interval */
long *smpls, /* out - number of samples */
int *cols) /* out - output column width */
{
extern char *optarg;
extern int optind;
extern int errno;
int c;
char *subopt;
long d;
int errflag = 0;
int i;
int src = 0;
char *host = local;
int sts;
char *endnum;
char *Sflag = NULL; /* argument of -S flag */
char *Tflag = NULL; /* argument of -T flag */
char *Aflag = NULL; /* argument of -A flag */
char *Oflag = NULL; /* argument of -O flag */
int zflag = 0; /* for -z */
char *tz = NULL; /* for -Z timezone */
int tzh; /* initial timezone handle */
struct timeval logStart;
struct timeval first;
pmMetricSpec *msp;
char *msg;
struct stat statbuf;
__pmSetAuthClient();
/* fill in default values */
cntxt->iall = 1;
cntxt->inum = 0;
cntxt->inames = NULL;
delta->tv_sec = 1;
delta->tv_usec = 0;
*smpls = 0;
*cols = 0;
/* extract command-line arguments */
while ((c = getopt(argc, argv, "A:a:D:dgh:i:n:O:p:rs:S:t:T:U:w:zZ:?")) != EOF) {
switch (c) {
case 'A': /* sample alignment */
Aflag = optarg;
break;
case 'a': /* interpolate archive */
if (++src > 1) {
fprintf(stderr, "%s: at most one of -a and -h allowed\n", pmProgname);
errflag++;
}
ahtype = PM_CONTEXT_ARCHIVE;
archive = optarg;
break;
case 'D': /* debug flag */
sts = __pmParseDebug(optarg);
if (sts < 0) {
fprintf(stderr, "%s: unrecognized debug flag specification (%s)\n",
pmProgname, optarg);
errflag++;
}
else
pmDebug |= sts;
break;
case 'd':
pauseFlag = 1;
break;
case 'g':
gui = 1;
break;
case 'h': /* host name */
if (++src > 1) {
fprintf(stderr, "%s: at most one of -a and -h allowed\n", pmProgname);
errflag++;
}
cntxt->host = host = optarg;
break;
case 'i': /* instance names */
cntxt->iall = 0;
i = cntxt->inum;
subopt = getinstance(optarg);
while (subopt != NULL) {
i++;
cntxt->inames =
(char **)realloc(cntxt->inames, i * (sizeof (char *)));
if (cntxt->inames == NULL) {
__pmNoMem("pmval.ip", i * sizeof(char *), PM_FATAL_ERR);
/*NOTREACHED*/
}
*(cntxt->inames + i - 1) = subopt;
subopt = getinstance(NULL);
}
cntxt->inum = i;
break;
case 'n': /* alternative name space file */
pmnsfile = optarg;
break;
case 'O': /* sample offset */
Oflag = optarg;
break;
case 'p': /* port for slave of existing pmtime master */
if ((sts = stat(optarg, &statbuf)) < 0) {
fprintf(stderr, "%s: Error: can not access time control port \"%s\": %s\n",
pmProgname, optarg, pmErrStr(-errno));
errflag++;
}
else if ((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
fprintf(stderr, "%s: Error: time control port \"%s\" is not a socket\n",
pmProgname, optarg);
errflag++;
}
else
control_port = optarg;
break;
case 'r': /* raw */
raw = 1;
break;
case 's': /* sample count */
d = (int)strtol(optarg, &endnum, 10);
if (Tflag) {
fprintf(stderr, "%s: at most one of -E and -T allowed\n", pmProgname);
errflag++;
}
else if (*endnum != '\0' || d < 0.0) {
fprintf(stderr, "%s: -s requires +ve numeric argument\n", pmProgname);
errflag++;
}
else *smpls = d;
break;
case 'S': /* start run time */
Sflag = optarg;
break;
case 't': /* sampling interval */
if (pmParseInterval(optarg, delta, &msg) < 0) {
fputs(msg, stderr);
free(msg);
errflag++;
}
break;
case 'T': /* run time */
if (*smpls != 0) {
fprintf(stderr, "%s: at most one of -T and -s allowed\n", pmProgname);
errflag++;
}
Tflag = optarg;
break;
case 'U': /* non-interpolated archive (undocumented) */
if (++src > 1) {
fprintf(stderr, "%s: at most one of -a, -h and -U allowed\n", pmProgname);
errflag++;
}
ahtype = PM_CONTEXT_ARCHIVE;
amode = PM_MODE_FORW;
archive = optarg;
rawarchive = 1;
break;
case 'w': /* output column width */
errno = 0;
d = atol(optarg);
if (errno || d < 1) errflag++;
else *cols = d;
break;
case 'z': /* timezone from host */
if (tz != NULL) {
fprintf(stderr, "%s: at most one of -Z and/or -z allowed\n", pmProgname);
errflag++;
}
zflag++;
break;
case 'Z': /* $TZ timezone */
if (zflag) {
fprintf(stderr, "%s: at most one of -Z and/or -z allowed\n", pmProgname);
errflag++;
}
tz = optarg;
break;
case '?':
fprintf(stderr, usage, pmProgname);
exit(EXIT_FAILURE);
/* NOTREACHED */
default:
errflag++;
}
}
/* parse uniform metric spec */
if (optind >= argc) {
fprintf(stderr, "Error: no metricname specified\n\n");
errflag++;
}
else if (optind < argc-1) {
fprintf(stderr, "Error: pmval can only process one metricname at a time\n\n");
errflag++;
}
else {
if (ahtype == PM_CONTEXT_HOST) {
if (pmParseMetricSpec(argv[optind], 0, host, &msp, &msg) < 0) {
fputs(msg, stderr);
free(msg);
errflag++;
}
}
else { /* must be archive */
if (pmParseMetricSpec(argv[optind], 1, archive, &msp, &msg) < 0) {
fputs(msg, stderr);
free(msg);
errflag++;
}
}
}
if (errflag) {
fprintf(stderr, usage, pmProgname);
exit(EXIT_FAILURE);
}
if (msp->isarch) {
archive = msp->source;
ahtype = PM_CONTEXT_ARCHIVE;
}
if (ahtype == PM_CONTEXT_ARCHIVE) {
if (gui == 1 && control_port != NULL) {
fprintf(stderr, "%s: -g cannot be used with -p\n", pmProgname);
errflag++;
}
if (gui == 1 && pauseFlag) {
fprintf(stderr, "%s: -g cannot be used with -d\n", pmProgname);
errflag++;
}
}
else {
if (pauseFlag) {
fprintf(stderr, "%s: -d can only be used with -a\n", pmProgname);
errflag++;
}
}
if (errflag) {
fprintf(stderr, usage, pmProgname);
exit(EXIT_FAILURE);
}
cntxt->metric = msp->metric;
if (msp->ninst > 0) {
cntxt->inum = msp->ninst;
cntxt->iall = (cntxt->inum == 0);
cntxt->inames = &msp->inst[0];
}
/* open connection to host */
if (msp->isarch == 0) {
if ((sts = pmNewContext(PM_CONTEXT_HOST, msp->source)) < 0) {
fprintf(stderr, "%s: Cannot connect to PMCD on host \"%s\": %s\n",
pmProgname, msp->source, pmErrStr(sts));
exit(EXIT_FAILURE);
}
cntxt->host = msp->source;
gettimeofday(&logStart, NULL);
}
/* open connection to archive */
else {
if ((sts = pmNewContext(PM_CONTEXT_ARCHIVE, msp->source)) < 0) {
fprintf(stderr, "%s: Cannot open archive \"%s\": %s\n",
pmProgname, msp->source, pmErrStr(sts));
exit(EXIT_FAILURE);
}
if ((sts = pmGetArchiveLabel(&label)) < 0) {
fprintf(stderr, "%s: Cannot get archive label record: %s\n",
pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
logStart = label.ll_start;
if ((sts = pmGetArchiveEnd(&last)) < 0) {
fprintf(stderr, "%s: Cannot determine end of archive: %s",
pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
}
if (zflag) {
if ((tzh = pmNewContextZone()) < 0) {
fprintf(stderr, "%s: Cannot set context timezone: %s\n",
pmProgname, pmErrStr(tzh));
exit(EXIT_FAILURE);
}
if (ahtype == PM_CONTEXT_ARCHIVE) {
printf("Note: timezone set to local timezone of host \"%s\" from archive\n\n",
label.ll_hostname);
rpt_tz_label = label.ll_hostname;
}
else {
printf("Note: timezone set to local timezone of host \"%s\"\n\n", host);
rpt_tz_label = host;
}
pmWhichZone(&rpt_tz);
}
else if (tz != NULL) {
if ((tzh = pmNewZone(tz)) < 0) {
fprintf(stderr, "%s: Cannot set timezone to \"%s\": %s\n",
pmProgname, tz, pmErrStr(tzh));
exit(EXIT_FAILURE);
}
printf("Note: timezone set to \"TZ=%s\"\n\n", tz);
pmWhichZone(&rpt_tz);
}
else printf("\n");
if (pmParseTimeWindow(Sflag, Tflag, Aflag, Oflag,
&logStart, &last,
&first, &last, posn, &msg) < 0) {
fprintf(stderr, msg);
exit(EXIT_FAILURE);
}
if (!gui &&
*smpls == 0 && last.tv_sec != INT_MAX && amode != PM_MODE_FORW)
*smpls = (long)((tosec(last) - tosec(*posn)) / tosec(*delta) + 1);
if (gui || control_port != NULL) {
/* set up pmtime control */
int mode;
if (msp->isarch)
mode = PM_TCTL_MODE_ARCHIVE;
else
mode = PM_TCTL_MODE_HOST;
pmtime.showdialog = 0; /* don't expose dialog yet */
if (gui) {
mode |= PM_TCTL_MODE_NEWMASTER;
control_port = mktemp("/usr/tmp/vcr.XXXXXX");
}
else
mode |= PM_TCTL_MODE_MASTER;
getXTBintervalFromTimeval(&pmtime.delta, &pmtime.vcrmode, delta);
if (msp->isarch) {
pmtime.position = *posn;
pmtime.start = first;
pmtime.finish = last;
}
else
gettimeofday(&pmtime.position, NULL);
if (rpt_tz == NULL) {
#if defined(IRIX6_5)
if (_MIPS_SYMBOL_PRESENT(__pmTimezone))
rpt_tz = __pmTimezone();
else
rpt_tz = getenv("TZ");
#else
rpt_tz = __pmTimezone();
#endif
if (msp->isarch) {
if ((sts = pmNewZone(rpt_tz)) < 0) {
fprintf(stderr, "%s: Cannot set timezone to \"%s\": %s\n",
pmProgname, rpt_tz, pmErrStr(sts));
exit(EXIT_FAILURE);
}
}
}
strncpy(pmtime.tz, rpt_tz, sizeof(pmtime.tz));
if (rpt_tz_label == NULL)
rpt_tz_label = "localhost";
strncpy(pmtime.tzlabel, rpt_tz_label, sizeof(pmtime.tzlabel));
if ((control_fd = pmTimeConnect(mode, control_port, &pmtime)) < 0) {
fprintf(stderr, "%s: pmTimeConnect: %s\n", pmProgname, pmErrStr(control_fd));
exit(EXIT_FAILURE);
}
gui = 1; /* means using pmtime control from here on */
}
else if (msp->isarch) {
/* archive, and no pmtime control, go it alone */
int tmp_ival;
int tmp_mode;
getXTBintervalFromTimeval(&tmp_ival, &tmp_mode, delta);
tmp_mode = (tmp_mode & 0xffff0000) | (amode & __PM_MODE_MASK);
if ((sts = pmSetMode(tmp_mode, posn, tmp_ival)) < 0) {
fprintf(stderr, "%s: pmSetMode: %s\n", pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
}
}
/***************************************************************************
* main
***************************************************************************/
int
main(int argc, char *argv[])
{
struct timeval delta; /* sample interval */
long smpls; /* number of samples */
int cols; /* width of output column */
struct timeval now; /* current task start time */
struct timeval sched; /* next task scheduled time */
Context cntxt; /* performance metric description */
pmResult *rslt1; /* current values */
pmResult *rslt2; /* previous values */
char *p;
int sts;
int first = 1; /* need first sample */
int forever;
int cmd;
int fetch;
int idx1;
int idx2;
int no_values = 0;
/* trim command name of leading directory components */
pmProgname = argv[0];
for (p = pmProgname; *p; p++) {
if (*p == '/')
pmProgname = p+1;
}
getargs(argc, argv, &cntxt, &now, &delta, &smpls, &cols);
forever = (smpls == 0 || gui);
initapi(&cntxt);
initinsts(&cntxt);
if (gui)
/*
* seems safe enough at this point to expose the Time Control
* dialog
*/
pmTimeShowDialog(1);
if (cols <= 0) cols = howide(cntxt.desc.type);
printhdr(&cntxt, smpls, delta, now);
/* wait till time for first sample */
if (archive == NULL )
sleeptill(now);
/* main loop fetching and printing sample values */
while (forever || (smpls-- > 0)) {
if (gui) {
for ( ; ; ) {
fetch = 0;
cmd = pmTimeRecv(&pmtime);
if (cmd < 0) {
fprintf(stderr, "\n%s: Time Control dialog has terminated: %s\n",
pmProgname, pmErrStr(cmd));
fprintf(stderr, "Sorry.\n");
exit(EXIT_FAILURE);
}
switch (cmd) {
case PM_TCTL_SET:
if (state == ENDLOG)
state = STANDBY;
else if (state == FORW)
state = START;
break;
case PM_TCTL_STEP:
if (pmtime.delta < 0) {
if (state != BACK) {
printf("\n[Time Control] Rewind/Reverse ...\n");
state = BACK;
}
}
else if (state != FORW && state != ENDLOG) {
if (ahtype == PM_CONTEXT_ARCHIVE) {
if (state != STANDBY)
printf("\n[Time Control] Repositioned in archive ...\n");
}
else {
printf("\n[Time Control] Resume ...\n");
}
if (pmtime.delta != lastdelta ||
PM_XTB_GET(pmtime.vcrmode) != PM_XTB_GET(lastdeltaunits))
printf("new interval: %1.2f sec\n",
getXTBinSeconds(&pmtime.delta, &pmtime.vcrmode));
if (ahtype == PM_CONTEXT_ARCHIVE) {
int setmode = PM_MODE_INTERP | (pmtime.vcrmode & 0xffff0000);
if ((sts = pmSetMode(setmode, &pmtime.position, pmtime.delta)) < 0) {
fprintf(stderr, "%s: pmSetMode: %s\n", pmProgname, pmErrStr(sts));
exit(EXIT_FAILURE);
}
}
lastdeltaunits = pmtime.vcrmode & 0xffff0000;
lastdelta = pmtime.delta;
state = FORW;
first = 1;
}
if (state == BACK || state == ENDLOG) {
/*
* for EOL and reverse travel, no pmFetch,
* so ack here
*/
ack_tctl(&pmtime.position);
break;
}
fetch = 1;
break;
case PM_TCTL_TZ:
if ((sts = pmNewZone(pmtime.tz)) < 0) {
fprintf(stderr, "%s: Warning: cannot set timezone to \"%s\": %s\n",
pmProgname, pmtime.tz, pmErrStr(sts));
}
break;
case PM_TCTL_VCRMODE:
/* something has changed ... suppress reporting */
if ((pmtime.vcrmode & __PM_MODE_MASK) == PM_TCTL_VCRMODE_DRAG)
state = MOVING;
else if (state != MOVING)
state = STANDBY;
break;
/*
* safely and silently ignore these
*/
case PM_TCTL_SHOWDIALOG:
break;
case PM_TCTL_SKIP:
case PM_TCTL_BOUNDS:
case PM_TCTL_ACK:
break;
default:
printf("pmTimeRecv: cmd %d?\n", cmd);
break;
}
if (fetch)
break;
}
}
if (first) {
if ((idx2 = getvals(&cntxt, &rslt2)) >= 0) {
/* first-time success */
first = 0;
if (cntxt.desc.indom != PM_INDOM_NULL)
printlabels(&cntxt, cols);
if (raw || (cntxt.desc.sem != PM_SEM_COUNTER)) {
if (gui || archive != NULL)
__pmPrintStamp(stdout, &rslt2->timestamp);
printvals(&cntxt, rslt2->vset[idx2], cols);
continue;
}
else if (no_values) {
if (gui || archive != NULL) {
__pmPrintStamp(stdout, &rslt2->timestamp);
printf(" ");
}
printf("No values available\n");
}
no_values = 0;
if (gui)
/* 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 (pauseFlag) {
sginap(toticks(delta));
}
else if (archive == NULL) {
sched = tadd(now,delta);
now = sched;
sleeptill(sched);
}
if (first)
/* keep trying */
continue;
/* next sample */
if ((idx1 = getvals(&cntxt, &rslt1)) == -2)
/* out the end of the window */
break;
else if (idx1 < 0) {
first = 1;
continue;
}
/* refresh instance names */
if (cntxt.iall && ! chkinsts(&cntxt, rslt1->vset[idx1])) {
free(cntxt.iids);
if (cntxt.iall)
free(cntxt.inames);
free(cntxt.ipairs);
initinsts(&cntxt);
printlabels(&cntxt, cols);
}
/* print values */
if (gui || archive != NULL)
__pmPrintStamp(stdout, &rslt1->timestamp);
if (raw || (cntxt.desc.sem != PM_SEM_COUNTER))
printvals(&cntxt, rslt1->vset[idx1], cols);
else
printrates(&cntxt, rslt1->vset[idx1], rslt1->timestamp,
rslt2->vset[idx2], rslt2->timestamp, cols);
/* discard previous and save current result */
pmFreeResult(rslt2);
rslt2 = rslt1;
idx2 = idx1;
}
exit(first == 0);
/*NOTREACHED*/
}