/* * Copyright (c) 2014 Red Hat. * Copyright (c) 1997,2004-2006 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2007 Aconex. 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 #include #include #include #include #include #include #include #include #include #include // Temporary buffer static char buffer[256]; // List of metrics static QmcGroup *group; static QList metrics; static bool isLive = false; static int numValues; static int doMetricType = PM_CONTEXT_HOST; static bool doMetricFlag = true; static double doMetricScale = 0.0; static QString doMetricSource; // Command line options static bool dumpFlag = true; static bool metricFlag; static bool niceFlag; static bool unitFlag; static bool sourceFlag; static bool timeFlag = true; static bool timeOffsetFlag; static bool rawFlag; static bool shortFlag; static bool descFlag; static bool widthFlag; static bool precFlag; static bool normFlag; static bool headerFlag; static bool fullFlag; static bool fullXFlag; static QString errStr = "?"; static QString timeFormat; static char delimiter = '\t'; static int precision = 3; static int width = 6; static int sampleCount; static int repeatLines; static pmLongOptions longopts[] = { PMAPI_GENERAL_OPTIONS, PMAPI_OPTIONS_HEADER("Reporting options"), { "config", 1, 'c', "FILE", "read list of metrics from FILE" }, { "check", 0, 'C', 0, "exit before dumping any values" }, { "delimiter", 1, 'd', "CHAR", "character separating each column" }, { "time-format", 1, 'f', "FMT", "time format string" }, { "fixed", 0, 'F', 0, "print fixed width values" }, { "scientific", 0, 'G', 0, "print values in scientific format if shorter" }, { "headers", 0, 'H', 0, "show all headers" }, { "interactive", 0, 'i', 0, "format columns for interactive use" }, { "source", 0, 'l', 0, "show source of metrics" }, { "metrics", 0, 'm', 0, "show metric names" }, { "", 0, 'M', 0, "show complete metrics names" }, { "", 0, 'N', 0, "show normalizing factor" }, { "offset", 0, 'o', 0, "prefix timestamp with offset in seconds" }, { "precision", 1, 'P', "N", "floating point precision [default 3]" }, { "repeat", 1, 'R', "N", "repeat the header after every N samples" }, { "raw", 0, 'r', 0, "output raw values, no rate conversion" }, { "unavailable", 1, 'U', "STR", "unavailable value string [default \"?\"]" }, { "units", 0, 'u', 0, "show metric units" }, { "width", 1, 'w', "N", "set column width" }, { "extended", 0, 'X', 0, "show complete metrics names (extended form)" }, PMAPI_OPTIONS_END }; // Collection start time static struct timeval logStartTime; // This may be putenv, so make it static static QString tzEnv = "TZ="; static QTextStream cerr(stderr); static QTextStream cout(stdout); static void checkUnits(QmcMetric *metric) { pmUnits units; const pmDesc &desc = metric->desc().desc(); // Only scale units if interactive and not raw if (rawFlag || !niceFlag) return; // Change to canonical bytes if (desc.units.dimTime == 0 && desc.units.dimSpace == 1 && desc.units.dimCount == 0 && desc.units.scaleSpace != PM_SPACE_BYTE) { units.dimSpace = 1; units.scaleSpace = PM_SPACE_BYTE; units.dimTime = units.dimCount = units.scaleTime = units.scaleCount = 0; metric->setScaleUnits(units); if (pmDebug & DBG_TRACE_APPL0) { cerr << "checkUnits: Changing " << metric->name() << " to use bytes" << endl; } } // Change to canonical count else if (desc.units.dimTime == 0 && desc.units.dimSpace == 0 && desc.units.dimCount == 1 && desc.units.scaleCount != PM_COUNT_ONE) { units.dimCount = 1; units.scaleCount = PM_COUNT_ONE; units.dimTime = units.dimSpace = units.scaleTime = units.scaleSpace = 0; metric->setScaleUnits(units); if (pmDebug & DBG_TRACE_APPL0) { cerr << "checkUnits: Changing " << metric->name() << " to use counts" << endl; } } else if (metric->desc().desc().sem == PM_SEM_COUNTER) { // Do time utilisation? if (desc.units.dimTime == 1 && desc.units.dimSpace == 0 && desc.units.dimCount == 0) { units.dimTime = 1; units.scaleTime = PM_TIME_SEC; units.dimSpace = units.dimCount = units.scaleSpace = units.scaleCount = 0; metric->setScaleUnits(units); if (pmDebug & DBG_TRACE_APPL0) { cerr << "checkUnits: Changing " << metric->name() << " to use time utilization" << endl; } } } } static void dometric(const char *name) { QString fullname = doMetricSource; if (fullname.length()) { if (doMetricType == PM_CONTEXT_ARCHIVE) fullname.append(QChar('/')); else fullname.append(QChar(':')); } fullname.append(name); QmcMetric* metric = group->addMetric((const char *)fullname.toAscii(), doMetricScale); if (metric->status() >= 0) { checkUnits(metric); metrics.append(metric); numValues += metric->numValues(); } else doMetricFlag = false; } static int traverse(const char *str, double scale) { pmMetricSpec *theMetric; char *msg; int sts = 0; sts = pmParseMetricSpec((char *)str, 0, (char *)0, &theMetric, &msg); if (sts < 0) { pmprintf("%s: Error: Unable to parse metric spec:\n%s\n", pmProgname, msg); free(msg); return sts; } // If the metric has instances, then it cannot be traversed if (theMetric->ninst) { QmcMetric *metric = group->addMetric(theMetric, scale); if (metric->status() >= 0) { checkUnits(metric); metrics.append(metric); numValues += metric->numValues(); } else sts = -1; } else { if (theMetric->isarch == 0) doMetricType = PM_CONTEXT_HOST; else if (theMetric->isarch == 1) doMetricType = PM_CONTEXT_ARCHIVE; else if (theMetric->isarch == 2) doMetricType = PM_CONTEXT_LOCAL; else { pmprintf("%s: Error: invalid metric source (%d): %s\n", pmProgname, theMetric->isarch, theMetric->metric); sts = -1; } doMetricSource = theMetric->source; if (sts >= 0) sts = group->use(doMetricType, doMetricSource); if (sts >= 0) { doMetricScale = scale; sts = pmTraversePMNS(theMetric->metric, dometric); if (sts >= 0 && doMetricFlag == false) sts = -1; else if (sts < 0) { pmprintf("%s: Error: %s: %s\n", pmProgname, theMetric->metric, pmErrStr(sts)); } } } free(theMetric); return sts; } // // parseConfig: parse list of metrics with optional scaling factor // static int parseConfig(QString const& configName, FILE *configFile) { char buf[1024]; char *last; char *msg; double scale = 0.0; int line = 0; int len = 0; int err = 0; while (!feof(configFile)) { if (fgets(buf, sizeof(buf), configFile) == NULL) break; len = strlen(buf); if (len == 0 || buf[0] == '#' || buf[0] == '\n') { line++; continue; } last = &buf[len-1]; if (*last != '\n' && !feof(configFile)) { pmprintf("%s: Line %d of %s was too long, skipping.\n", pmProgname, line, (const char *)configName.toAscii()); while(buf[len-1] != '\n') { if (fgets(buf, sizeof(buf), configFile) == NULL) break; len = strlen(buf); } err++; continue; } if (*last == '\n') *last = '\0'; line++; last = strrchr(buf, ']'); if (last == NULL) { // No instances for (last = buf; *last != '\0' && isspace(*last); last++) { ; } if (*last == '\0') continue; for (; *last != '\0' && !isspace(*last); last++) { ; } last--; } if (*(last + 1) == '\0') { scale = 0.0; } else { *(last+1)='\0'; scale = strtod(last+2, &msg); if (*msg != '\0') { pmprintf("%s: Line %d of %s has an illegal scaling factor, assuming 0.\n", pmProgname, line, (const char *)configName.toAscii()); err++; scale = 0.0; } } if (pmDebug & DBG_TRACE_APPL0) cerr << "parseConfig: Adding metric '" << buf << "' with scale = " << scale << " from line " << line << endl; if (traverse(buf, scale) < 0) err++; } if (configFile != stdin) fclose(configFile); return err; } static const char * dumpTime(struct timeval const &curPos) { time_t curTime = (time_t)(curPos.tv_sec); char *p; if (timeOffsetFlag) { double o = __pmtimevalSub(&curPos, &logStartTime); if (o < 10) sprintf(buffer, "%.2f ", o); else if (o < 100) sprintf(buffer, "%.1f ", o); else sprintf(buffer, "%.0f ", o); for (p = buffer; *p != ' '; p++) ; *p++ = delimiter; } else p = buffer; if (timeFormat.length() > 0) strftime(p, sizeof(buffer) - (p-buffer), (const char *)(timeFormat.toAscii()), localtime(&curTime)); else { // Use ctime as we have put the timezone into the environment strcpy(p, ctime(&curTime)); p[19] = '\0'; } return buffer; } static void dumpHeader() { static bool fullOnce = false; QmcMetric *metric; bool instFlag = false; QString noneStr = "none"; QString srcStr = "Source"; QString metricStr = "Metric"; QString instStr = "Inst"; QString normStr = "Normal"; QString unitStr = "Units"; QString columnStr = "Column"; const char *timeStr; int m; int i; int c; int v; int p; int len = 0; if (niceFlag) { struct timeval pos = { 0, 0 }; timeStr = dumpTime(pos); len = strlen(timeStr); } if (fullFlag) { fullOnce = true; for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; for (i = 0; i < metric->numValues(); i++, v++) { cout << '[' << qSetFieldWidth(2) << v << qSetFieldWidth(0) << "] " << metric->spec(sourceFlag, true, i) << endl; } } cout << endl; } if (fullOnce) { if (timeFlag) { if (len < columnStr.length()) { columnStr.remove(len, columnStr.length() - len); } cout << qSetFieldWidth(len) << columnStr << qSetFieldWidth(0) << delimiter; } for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; for (i = 0; i < metric->numValues(); i++) { cout << qSetFieldWidth(width) << v << qSetFieldWidth(0); if (v < numValues) { cout << delimiter; v++; } } } cout << endl; } if (niceFlag && sourceFlag) { if (timeFlag) { if (len < srcStr.length()) { srcStr.remove(len, srcStr.length() - len); } cout << qSetFieldWidth(len) << srcStr << qSetFieldWidth(0) << delimiter; } for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; QString const& str = metric->context()->source().host(); strncpy(buffer, (const char *)str.toAscii(), width); buffer[width] = '\0'; for (i = 0; i < metric->numValues(); i++) { cout << qSetFieldWidth(width) << buffer << qSetFieldWidth(0); if (v < numValues) { cout << delimiter; v++; } } } cout << endl; } if (metricFlag || (sourceFlag && !niceFlag)) { if (timeFlag) { if (niceFlag) { if (len < metricStr.length()) { metricStr.remove(len, metricStr.length() - len); } cout << qSetFieldWidth(len) << metricStr << qSetFieldWidth(0); cout << delimiter; } else cout << "Time" << delimiter; } for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; if (niceFlag && !instFlag && metric->hasInstances()) instFlag = true; for (i = 0; i < metric->numValues(); i++) { if (niceFlag) { QString const &str = metric->spec(false, false, i); p = str.length() - width; if (p > 0) { for (c = (p - 1 > 0 ? p - 1 : 0); c < str.length(); c++) { if (str[c] == '.') { c++; break; } } if (c < str.length()) cout << qSetFieldWidth(width) << ((const char *)str.toAscii() + c) << qSetFieldWidth(0); else cout << qSetFieldWidth(width) << ((const char *)str.toAscii() + p) << qSetFieldWidth(0); } else { cout << qSetFieldWidth(width) << str << qSetFieldWidth(0); } } else cout << metric->spec(sourceFlag, true, i); if (v < numValues) { cout << delimiter; v++; } } } cout << endl; } if (instFlag) { if (timeFlag) { if (niceFlag) { if (len < instStr.length()) { instStr.remove(len, instStr.length() - len); } cout << qSetFieldWidth(len) << instStr << qSetFieldWidth(0) << delimiter; } else { cout << qSetFieldWidth(len) << errStr << qSetFieldWidth(0) << delimiter; } } for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; for (i = 0; i < metric->numValues(); i++) { if (metric->hasInstances()) { QString const &str = metric->instName(i); strncpy(buffer, (const char *)str.toAscii(), width); buffer[width] = '\0'; cout << qSetFieldWidth(width) << buffer << qSetFieldWidth(0); } else cout << qSetFieldWidth(width) << "n/a" << qSetFieldWidth(0); if (v < numValues) { cout << delimiter; v++; } } } cout << endl; } if (normFlag) { if (timeFlag) { if (niceFlag) { if (len < normStr.length()) { normStr.remove(len, normStr.length() - len); } cout << qSetFieldWidth(len) << normStr << qSetFieldWidth(0) << delimiter; } else cout << errStr << delimiter; } for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; for (i = 0; i < metric->numValues(); i++) { if (shortFlag) cout << qSetRealNumberPrecision(precision) << qSetFieldWidth(width) << metric->scale() << qSetFieldWidth(0); else if (descFlag) cout << qSetFieldWidth(width) << QmcMetric::formatNumber(metric->scale()) << qSetFieldWidth(0); else cout << fixed << qSetRealNumberPrecision(precision) << qSetFieldWidth(width) << metric->scale() << qSetFieldWidth(0); if (v < numValues) { cout << delimiter; v++; } } } cout << endl; } if (unitFlag) { if (timeFlag) { if (niceFlag) { if (len < unitStr.length()) { unitStr.remove(len, unitStr.length() - len); } cout << qSetFieldWidth(len) << unitStr << qSetFieldWidth(0) << delimiter; } else cout << noneStr << delimiter; } for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; QString const &str = (niceFlag ? metric->desc().shortUnits() : metric->desc().units()); for (i = 0; i < metric->numValues(); i++) { if (niceFlag) if (str.length() > width) cout << qSetFieldWidth(width) << ((const char *)str.toAscii() + str.length() - width) << qSetFieldWidth(0); else cout << qSetFieldWidth(width) << str << qSetFieldWidth(0); else cout << str; if (v < numValues) { cout << delimiter; v++; } } } cout << endl; } } /* * Get Extended Time Base interval and Units from a timeval */ #define SECS_IN_24_DAYS 2073600.0 static int getXTBintervalFromTimeval(int *mode, struct timeval *tval) { double tmp_ival = tval->tv_sec + tval->tv_usec / 1000000.0; if (tmp_ival > SECS_IN_24_DAYS) { *mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_SEC); return ((int)tmp_ival); } else { *mode = (*mode & 0x0000ffff) | PM_XTB_SET(PM_TIME_MSEC); return ((int)(tmp_ival * 1000.0)); } } 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; } 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; } static struct timespec * tospec(struct timeval tv, struct timespec *ts) { ts->tv_nsec = tv.tv_usec * 1000; ts->tv_sec = tv.tv_sec; return ts; } static void sleeptill(struct timeval sched) { int sts; struct timeval curr; /* current time */ struct timespec delay; /* interval to sleep */ struct timespec left; /* remaining sleep time */ __pmtimevalNow(&curr); tospec(tsub(sched, curr), &delay); for (;;) { /* loop to catch early wakeup by nanosleep */ sts = nanosleep(&delay, &left); if (sts == 0 || (sts < 0 && errno != EINTR)) break; delay = left; } } static int override(int opt, pmOptions *opts) { (void)opts; if (opt == 'H' || opt == 'a' || opt == 'N') return 1; return 0; } int main(int argc, char *argv[]) { char *endnum = NULL; int sts = 0; int c, l, m, i, v; int lines = 0; // Metrics QmcMetric *metric; double value = 0; // Config file QString configName; FILE *configFile = NULL; // Timing QString tzLabel; QString tzString; struct timeval logEndTime; double endTime; double delay; double pos; // Parse command line options // pmOptions opts; memset(&opts, 0, sizeof(opts)); opts.flags = PM_OPTFLAG_MULTI; opts.short_options = PMAPI_OPTIONS "c:Cd:f:FGHilmMNoP:rR:uU:w:X"; opts.long_options = longopts; opts.short_usage = "[options] [metrics ...]"; opts.override = override; while ((c = pmGetOptions(argc, argv, &opts)) != EOF) { switch (c) { case 'a': // archive name endnum = strtok(opts.optarg, ", \t"); while (endnum) { __pmAddOptArchive(&opts, endnum); endnum = strtok(NULL, ", \t"); } break; case 'c': // config file configName = opts.optarg; break; case 'C': // parse config, output metrics and units only dumpFlag = false; break; case 'd': // delimiter if (strlen(opts.optarg) == 2 && opts.optarg[0] == '\\') { switch (opts.optarg[1]) { case 'n': delimiter = '\n'; break; case 't': delimiter = '\t'; break; default: delimiter = ' '; } } else if (strlen(opts.optarg) > 1) { pmprintf("%s: delimiter must be one character\n", pmProgname); opts.errors++; } else delimiter = opts.optarg[0]; break; case 'f': // Time format timeFormat = opts.optarg; if (timeFormat.length() == 0) timeFlag = false; else timeFlag = true; break; case 'F': // Fixed width values if (shortFlag) { pmprintf("%s: -F and -G options may not be used together\n", pmProgname); opts.errors++; } else descFlag = true; break; case 'G': // Shortest format if (descFlag) { pmprintf("%s: -F and -G may not be used together\n", pmProgname); opts.errors++; } else if (niceFlag) { pmprintf("%s: -i and -G may not be used togther\n", pmProgname); opts.errors++; } else shortFlag = true; break; case 'H': // show all headers headerFlag = true; break; case 'i': // abbreviate metric names if (precFlag) { pmprintf("%s: -i and -P may not be used togther\n", pmProgname); opts.errors++; } else if (shortFlag) { pmprintf("%s: -i and -G may not be used togther\n", pmProgname); opts.errors++; } else niceFlag = true; break; case 'l': // show source of metrics sourceFlag = true; break; case 'm': // show metric names metricFlag = true; break; case 'M': // show full metric names fullFlag = true; break; case 'X': // show full metric names (extended mode) fullFlag = true; fullXFlag = true; break; case 'N': // show normalization values normFlag = true; break; case 'o': // report timeOffset timeOffsetFlag = true; break; case 'P': // precision if (widthFlag) { pmprintf("%s: -P and -w may not be used together\n", pmProgname); opts.errors++; } else if (niceFlag) { pmprintf("%s: -i and -P may not be used together\n", pmProgname); opts.errors++; } else { precision = (int)strtol(opts.optarg, &endnum, 10); precFlag = true; if (*endnum != '\0' || precision < 0) { pmprintf("%s: -P requires a positive numeric argument\n", pmProgname); opts.errors++; } } break; case 'r': // output raw values rawFlag = true; break; case 'R': // repeat header repeatLines = (int)strtol(opts.optarg, &endnum, 10); if (*endnum != '\0' || repeatLines <= 0) { pmprintf("%s: -R requires a positive numeric argument\n", pmProgname); opts.errors++; } break; case 'u': // show units unitFlag = true; break; case 'U': // error string errStr = opts.optarg; break; case 'w': // width if (precFlag) { pmprintf("%s: -P and -w may not be used together\n", pmProgname); opts.errors++; } else { width = (int)strtol(opts.optarg, &endnum, 10); widthFlag = true; if (*endnum != '\0' || width < 0) { pmprintf("%s: -w requires a positive numeric argument\n", pmProgname); opts.errors++; } else if (width < 3) { pmprintf("%s: -w must be greater than 2\n", pmProgname); opts.errors++; } } break; } } if (opts.context == PM_CONTEXT_HOST) { if (opts.nhosts > 1) { pmprintf("%s: only one host may be specified\n", pmProgname); opts.errors++; } } if (opts.errors > 0) { pmUsageMessage(&opts); exit(1); } // Default update interval is 1 second if (opts.interval.tv_sec == 0 && opts.interval.tv_usec == 0) opts.interval.tv_sec = 1; if (headerFlag) metricFlag = unitFlag = sourceFlag = normFlag = true; if (fullXFlag) niceFlag = true; // Create the metric fetch group group = new QmcGroup(true); // Create archive contexts if (opts.narchives > 0) { for (c = 0; c < opts.narchives; c++) if (group->use(PM_CONTEXT_ARCHIVE, opts.archives[c]) < 0) opts.errors++; } // Create live context else if (opts.nhosts > 0) { if (group->use(PM_CONTEXT_HOST, opts.hosts[0]) < 0) opts.errors++; } if (opts.errors) { pmflush(); exit(1); } // Set up cout to use the required formatting // if (niceFlag) { width = width < 6 ? 6 : width; widthFlag = true; descFlag = true; } if (shortFlag) { if (widthFlag) { width = width < 3 ? 3 : width; precision = width - 1; } else { precision = precision < 2 ? 2 : precision; width = precision + 1; } } else { if (widthFlag) { width = width < 3 ? 3 : width; precision = width - 2; } else { precision = precision < 2 ? 2 : precision; width = precision + 2; } } if (pmDebug & DBG_TRACE_APPL0) cerr << "main: optind = " << opts.optind << ", argc = " << argc << ", width = " << width << ", precision = " << precision << endl; if (opts.optind == argc) { if (configName.length() == 0) { configFile = stdin; configName = "(stdin)"; } else { configFile = fopen((const char *)configName.toAscii(), "r"); if (configFile == NULL) { pmprintf("%s: Unable to open %s: %s\n", pmProgname, (const char *)configName.toAscii(), strerror(errno)); pmflush(); exit(1); } } } else if (configName.length()) { pmprintf("%s: configuration file cannot be specified with metrics\n", pmProgname); exit(1); } if (configFile != NULL) { opts.errors = parseConfig(configName, configFile); } else { for (c = opts.optind; c < argc; c++) { if (traverse(argv[c], 0.0) < 0) opts.errors++; } } if (metrics.size() == 0 || numValues == 0) { pmprintf("%s: no valid metrics, exiting.\n", pmProgname); pmflush(); exit(1); } else if (opts.errors) pmprintf("%s: Warning: Some metrics ignored, continuing with valid metrics\n", pmProgname); pmflush(); if (pmDebug & DBG_TRACE_APPL0) cerr << "main: parsed " << metrics.size() << " metrics" << endl; group->useDefault(); if (group->context()->source().type() != PM_CONTEXT_ARCHIVE) isLive = true; if (pmDebug & DBG_TRACE_APPL0) cerr << "main: default source is " << *(group->context()) << endl; if (opts.tzflag) group->useTZ(); else if (opts.timezone) { sts = group->useTZ(opts.timezone); if ((sts = pmNewZone(opts.timezone)) < 0) { pmprintf("%s: cannot set timezone to \"%s\": %s\n", pmProgname, opts.timezone, pmErrStr(sts)); pmflush(); exit(1); } } group->defaultTZ(tzLabel, tzString); if (pmDebug & DBG_TRACE_APPL0) { cerr << "main: Using timezone \"" << tzString << "\" from " << tzLabel << endl; } // putenv timezone into TZ as we may use strftime or ctime later // if (group->defaultTZ() != QmcGroup::localTZ) { tzEnv.append(tzString); sts = putenv(strdup((const char *)tzEnv.toAscii())); if (sts < 0) { pmprintf("%s: Warning: Unable to set timezone in environment\n", pmProgname); sts = 0; } else if (pmDebug & DBG_TRACE_APPL0) cerr << "main: Changed environment with \"" << tzEnv << '"' << endl; } if (isLive) { __pmtimevalNow(&logStartTime); logEndTime.tv_sec = INT_MAX; logEndTime.tv_usec = INT_MAX; } else { group->updateBounds(); logStartTime = group->logStart(); logEndTime = group->logEnd(); if (__pmtimevalToReal(&logEndTime) <= __pmtimevalToReal(&logStartTime)) { logEndTime.tv_sec = INT_MAX; logEndTime.tv_usec = INT_MAX; } } if (pmDebug & DBG_TRACE_APPL0) { cerr << "main: start = " << __pmtimevalToReal(&logStartTime) << ", end = " << __pmtimevalToReal(&logEndTime) << endl; } sts = pmParseTimeWindow(opts.start_optarg, opts.finish_optarg, opts.align_optarg, opts.origin_optarg, &logStartTime, &logEndTime, &opts.start, &opts.finish, &opts.origin, &endnum); if (sts < 0) { pmprintf("%s\n", endnum); pmUsageMessage(&opts); exit(1); } pos = __pmtimevalToReal(&opts.origin); endTime = __pmtimevalToReal(&opts.finish); delay = (int)(__pmtimevalToReal(&opts.interval) * 1000.0); if (endTime < pos && opts.finish_optarg == NULL) endTime = DBL_MAX; if (pmDebug & DBG_TRACE_APPL0) { cerr << "main: realStartTime = " << __pmtimevalToReal(&opts.start) << ", endTime = " << endTime << ", pos = " << pos << ", delay = " << delay << endl; } pmflush(); dumpHeader(); // Only dump full names once if (fullXFlag == false) fullFlag = false; if (!dumpFlag) exit(0); if (!isLive) { int tmp_mode = PM_MODE_INTERP; int tmp_delay = getXTBintervalFromTimeval(&tmp_mode, &opts.interval); group->setArchiveMode(tmp_mode, &opts.origin, tmp_delay); } if (shortFlag) { cout.setRealNumberPrecision(precision); } else if (!descFlag) { cout.setRealNumberPrecision(precision); cout.setRealNumberNotation(QTextStream::FixedNotation); } while (pos <= endTime && ((opts.samples > 0 && sampleCount < opts.samples) || opts.samples == 0)) { group->fetch(); sampleCount++; if (timeFlag) cout << dumpTime(opts.origin) << delimiter; for (m = 0, v = 1; m < metrics.size(); m++) { metric = metrics[m]; for (i = 0; i < metric->numValues(); i++) { if (rawFlag) { if (metric->currentError(i) < 0) { if (niceFlag) cout << qSetFieldWidth(width) << errStr << qSetFieldWidth(0); else cout << errStr; goto next; } else if (metric->real()) value = metric->currentValue(i); } else if (metric->error(i) < 0) { if (niceFlag) cout << qSetFieldWidth(width) << errStr << qSetFieldWidth(0); else cout << errStr; goto next; } else if (metric->real()) value = metric->value(i); if (metric->real()) { if (descFlag) if (niceFlag) cout << qSetFieldWidth(width) << QmcMetric::formatNumber(value) << qSetFieldWidth(0); else cout << QmcMetric::formatNumber(value); else if (niceFlag) cout << qSetFieldWidth(width) << value << qSetFieldWidth(0); else cout << value; } // String else { l = metric->stringValue(i).length(); buffer[0] = '\"'; if (niceFlag) { if (l > width - 2) { strncpy(buffer+1, (const char *)metric->stringValue(i).toAscii(), width - 2); buffer[width - 1] = '\"'; buffer[width] = '\0'; cout << qSetFieldWidth(width) << buffer << qSetFieldWidth(0); } else { strcpy(buffer+1, (const char *)metric->stringValue(i).toAscii()); buffer[l + 1] = '\"'; buffer[l + 2] = '\0'; cout << qSetFieldWidth(width) << buffer; } } else if (widthFlag) { if (l > width - 2 && width > 5) { strncpy(buffer+1, (const char *)metric->stringValue(i).toAscii(), width - 5); strcpy(buffer + width - 4, "...\""); buffer[width] = '\0'; cout << qSetFieldWidth(width) << buffer << qSetFieldWidth(0); } else { strncpy(buffer+1, (const char *)metric->stringValue(i).toAscii(), width - 2); buffer[width - 1] = '\"'; buffer[width] = '\0'; cout << qSetFieldWidth(width) << buffer << qSetFieldWidth(0); } } else cout << '\"' << metric->stringValue(i) << '\"'; } next: if (v < numValues) { cout << delimiter; v++; } } } cout << endl; // if (opts.samples > 0 && sampleCount == opts.samples) // continue; /* do not sleep needlessly */ opts.origin = tadd(opts.origin, opts.interval); if (isLive) sleeptill(opts.origin); pos = __pmtimevalToReal(&opts.origin); lines++; if (repeatLines > 0 && repeatLines == lines) { cout << endl; dumpHeader(); lines = 0; } } return 0; }