From 47e6e7c84f008a53061e661f31ae96629bc694ef Mon Sep 17 00:00:00 2001 From: Igor Pashev Date: Sun, 26 Oct 2014 12:33:50 +0400 Subject: Debian 3.9.10 --- src/pmdumptext/GNUmakefile | 36 ++ src/pmdumptext/pmdumptext.cpp | 1262 +++++++++++++++++++++++++++++++++++++++++ src/pmdumptext/pmdumptext.pro | 11 + 3 files changed, 1309 insertions(+) create mode 100644 src/pmdumptext/GNUmakefile create mode 100644 src/pmdumptext/pmdumptext.cpp create mode 100644 src/pmdumptext/pmdumptext.pro (limited to 'src/pmdumptext') diff --git a/src/pmdumptext/GNUmakefile b/src/pmdumptext/GNUmakefile new file mode 100644 index 0000000..f7b7ddf --- /dev/null +++ b/src/pmdumptext/GNUmakefile @@ -0,0 +1,36 @@ +TOPDIR = ../.. +COMMAND = pmdumptext +PROJECT = $(COMMAND).pro +include $(TOPDIR)/src/include/builddefs + +QRCFILE = $(COMMAND).qrc +UIFILES = $(shell echo *.ui) +SOURCES = pmdumptext.cpp +LSRCFILES = $(PROJECT) $(SOURCES) +LDIRT = $(COMMAND) + +default: build-me + +include $(BUILDRULES) + +ifeq "$(ENABLE_QT)" "true" +build-me: + $(QTMAKE) + $(LNMAKE) + +install: default +ifeq ($(WINDOW),mac) + $(call INSTALL_QT_FRAMEWORKS,$(BINARY)) + $(INSTALL) -m 755 $(BINARY) $(PCP_BIN_DIR)/$(COMMAND) + rm $(BINARY) +else + $(INSTALL) -m 755 $(BINARY) $(PCP_BIN_DIR)/$(COMMAND) +endif +else +build-me: +install: +endif + +default_pcp: default + +install_pcp: install diff --git a/src/pmdumptext/pmdumptext.cpp b/src/pmdumptext/pmdumptext.cpp new file mode 100644 index 0000000..8d99bae --- /dev/null +++ b/src/pmdumptext/pmdumptext.cpp @@ -0,0 +1,1262 @@ +/* + * 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; +} diff --git a/src/pmdumptext/pmdumptext.pro b/src/pmdumptext/pmdumptext.pro new file mode 100644 index 0000000..d629717 --- /dev/null +++ b/src/pmdumptext/pmdumptext.pro @@ -0,0 +1,11 @@ +TEMPLATE = app +LANGUAGE = C++ +SOURCES = pmdumptext.cpp +CONFIG += qt console warn_on +INCLUDEPATH += ../include ../libpcp_qmc/src +release:DESTDIR = build/debug +debug:DESTDIR = build/release +LIBS += -L../libpcp/src +LIBS += -L../libpcp_qmc/src -L../libpcp_qmc/src/$$DESTSIR +LIBS += -lpcp_qmc -lpcp +QT -= gui -- cgit v1.2.3