summaryrefslogtreecommitdiff
path: root/src/libpcp_qmc/src/qmc_metric.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp_qmc/src/qmc_metric.cpp')
-rw-r--r--src/libpcp_qmc/src/qmc_metric.cpp1248
1 files changed, 1248 insertions, 0 deletions
diff --git a/src/libpcp_qmc/src/qmc_metric.cpp b/src/libpcp_qmc/src/qmc_metric.cpp
new file mode 100644
index 0000000..85d5016
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_metric.cpp
@@ -0,0 +1,1248 @@
+/*
+ * Copyright (c) 1997,2005 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <strings.h>
+#include "qmc_metric.h"
+#include "qmc_group.h"
+
+QmcMetricValue::QmcMetricValue()
+{
+ my.value = 0.0;
+ my.currentValue = 0.0;
+ my.previousValue = 0.0;
+ my.error = PM_ERR_VALUE;
+ my.currentError = PM_ERR_VALUE;
+ my.previousError = PM_ERR_VALUE;
+ my.instance = PM_ERR_INST;
+}
+
+QmcMetricValue const&
+QmcMetricValue::operator=(QmcMetricValue const& rhs)
+{
+ if (this != &rhs) {
+ my.instance = rhs.my.instance;
+ my.value = rhs.my.value;
+ my.currentValue = rhs.my.currentValue;
+ my.previousValue = rhs.my.previousValue;
+ my.stringValue = rhs.my.stringValue;
+ my.error = rhs.my.error;
+ my.currentError = rhs.my.currentError;
+ my.previousError = rhs.my.previousError;
+ }
+ return *this;
+}
+
+QmcMetric::QmcMetric(QmcGroup *group, const char *string,
+ double scale, bool active)
+{
+ pmMetricSpec *metricSpec;
+ char *msg;
+
+ my.status = 0;
+ my.group = group;
+ my.scale = scale;
+ my.idIndex = UINT_MAX;
+ my.indomIndex = UINT_MAX;
+ my.contextIndex = UINT_MAX;
+ my.explicitInst = false;
+ my.active = active;
+
+ my.status = pmParseMetricSpec(string, 0, NULL, &metricSpec, &msg);
+ if (my.status < 0) {
+ pmprintf("%s: Error: Unable to parse metric spec:\n%s\n",
+ pmProgname, msg);
+ my.name = QString::null;
+ free(msg);
+ }
+ else {
+ my.name = QString(metricSpec->metric);
+ setup(group, metricSpec);
+ free(metricSpec);
+ }
+}
+
+QmcMetric::QmcMetric(QmcGroup *group, pmMetricSpec *metricSpec,
+ double scale, bool active)
+{
+ my.pmid = PM_ID_NULL;
+ my.status = 0;
+ my.name = QString(metricSpec->metric);
+ my.group = group;
+ my.scale = scale;
+ my.contextIndex = UINT_MAX;
+ my.idIndex = 0;
+ my.indomIndex = UINT_MAX;
+ my.explicitInst = false;
+ my.active = active;
+ setup(group, metricSpec);
+}
+
+void
+QmcMetric::setup(QmcGroup* group, pmMetricSpec *metricSpec)
+{
+ if (my.status >= 0)
+ setupDesc(group, metricSpec);
+ if (my.status >= 0)
+ setupIndom(metricSpec);
+ if (my.status < 0)
+ return;
+ if (pmDebug & DBG_TRACE_PMC)
+ dumpAll();
+}
+
+QmcMetric::~QmcMetric()
+{
+ if (hasInstances())
+ for (int i = 0; i < my.values.size(); i++)
+ indom()->removeRef(my.values[i].instance());
+}
+
+void
+QmcMetric::setupDesc(QmcGroup* group, pmMetricSpec *metricSpec)
+{
+ int contextType = PM_CONTEXT_HOST;
+ int descType;
+ char *src = NULL;
+ char *name = NULL;
+
+ if (metricSpec->isarch == 1)
+ contextType = PM_CONTEXT_ARCHIVE;
+ else if (metricSpec->isarch == 2)
+ contextType = PM_CONTEXT_LOCAL;
+
+ QString source = QString(metricSpec->source);
+ my.status = group->use(contextType, source);
+
+ if (my.status >= 0) {
+ my.contextIndex = group->contextIndex();
+ contextType = context()->source().type();
+ my.status = context()->lookupPMID(metricSpec->metric, my.pmid);
+ if (my.status >= 0)
+ my.status = context()->lookupInDom(my.pmid, my.indomIndex);
+ if (my.status < 0) {
+ name = strdup(nameAscii());
+ src = strdup(context()->source().sourceAscii());
+ pmprintf("%s: Error: %s%c%s: %s\n",
+ pmProgname,
+ contextType == PM_CONTEXT_LOCAL ? "@" : src,
+ contextType == PM_CONTEXT_ARCHIVE ? '/' : ':',
+ name, pmErrStr(my.status));
+ }
+ }
+ else {
+ // do nothing, error already reported via pmprintf from
+ // QmcGroup::use()
+ ;
+ }
+
+ if (my.status >= 0) {
+ descType = desc().desc().type;
+ if (descType == PM_TYPE_NOSUPPORT) {
+ my.status = PM_ERR_CONV;
+ name = strdup(nameAscii());
+ src = strdup(context()->source().sourceAscii());
+ pmprintf("%s: Error: %s%c%s is not supported on %s\n",
+ pmProgname, contextType == PM_CONTEXT_LOCAL ? "@" : src,
+ (contextType == PM_CONTEXT_ARCHIVE ? '/' : ':'),
+ name, context()->source().hostAscii());
+ }
+ else if (descType == PM_TYPE_AGGREGATE ||
+ descType == PM_TYPE_AGGREGATE_STATIC ||
+ descType == PM_TYPE_UNKNOWN) {
+ my.status = PM_ERR_CONV;
+ name = strdup(nameAscii());
+ src = strdup(context()->source().sourceAscii());
+ pmprintf("%s: Error: %s%c%s has type \"%s\","
+ " which is not a number or a string\n",
+ pmProgname, contextType == PM_CONTEXT_LOCAL ? "@" : src,
+ (contextType == PM_CONTEXT_ARCHIVE ? '/' : ':'),
+ name, pmTypeStr(descType));
+ }
+ }
+
+ if (name)
+ free(name);
+ if (src)
+ free(src);
+}
+
+void
+QmcMetric::setupIndom(pmMetricSpec *metricSpec)
+{
+ int i, j;
+ QmcIndom *indomPtr = indom();
+
+ if (!hasIndom()) {
+ if (metricSpec->ninst > 0) {
+ my.status = PM_ERR_INST;
+ dumpErr(metricSpec->inst[0]);
+ }
+ else
+ setupValues(1);
+ }
+ else if (metricSpec->ninst) {
+ Q_ASSERT(hasInstances());
+ setupValues(metricSpec->ninst);
+
+ for (i = 0 ; i < metricSpec->ninst && my.status >= 0; i++) {
+ j = indomPtr->lookup(metricSpec->inst[i]);
+ if (j >= 0)
+ my.values[i].setInstance(j);
+ else {
+ my.status = PM_ERR_INST;
+ my.values[i].setInstance(PM_ERR_INST);
+ dumpErr(metricSpec->inst[i]);
+ }
+ }
+ my.explicitInst = true;
+ }
+ else {
+ Q_ASSERT(hasInstances());
+
+ if (my.active) {
+ setupValues(indomPtr->numActiveInsts());
+ indomPtr->refAll(my.active);
+
+ for (i = 0, j = 0; i < indomPtr->listLen(); i++)
+ if (!indomPtr->nullInst(i) && indomPtr->activeInst(i))
+ my.values[j++].setInstance(i);
+ }
+ else {
+ setupValues(indomPtr->numInsts());
+ indomPtr->refAll(my.active);
+
+ for (i = 0, j = 0; i < indomPtr->listLen(); i++)
+ if (!indomPtr->nullInst(i))
+ my.values[j++].setInstance(i);
+ }
+ }
+}
+
+void
+QmcMetric::setupValues(int num)
+{
+ int i, oldLen = my.values.size();
+
+ if (num == 0)
+ my.values.clear();
+ else {
+ if (my.values.size() > num)
+ for (i = num; i < my.values.size(); i++)
+ my.values.removeAt(i);
+ for (i = oldLen; i < num; i++)
+ my.values.append(QmcMetricValue());
+ }
+}
+
+QString
+QmcMetric::spec(bool srcFlag, bool instFlag, uint instance) const
+{
+ QString str;
+ int i, len = 4;
+
+ if (srcFlag)
+ len += context()->source().source().size();
+ len += name().size();
+ if (hasInstances() && instFlag) {
+ if (instance != UINT_MAX)
+ len += instName(instance).size() + 2;
+ else
+ for (i = 0; i < numInst(); i++)
+ len += instName(i).size() + 4;
+ }
+
+ if (srcFlag) {
+ str.append(context()->source().source());
+ if (context()->source().type() == PM_CONTEXT_ARCHIVE)
+ str.append(QChar('/'));
+ else
+ str.append(QChar(':'));
+ }
+ str.append(name());
+ if (hasInstances() && instFlag) {
+ str.append(QChar('['));
+ str.append(QChar('\"'));
+ if (instance != UINT_MAX)
+ str.append(instName(instance));
+ else if (numInst()) {
+ str.append(instName(0));
+ for (i = 1; i < numInst(); i++) {
+ str.append("\", \"");
+ str.append(instName(i));
+ }
+ }
+ str.append("\"]");
+ }
+
+ return str;
+}
+
+void
+QmcMetric::dumpSource(QTextStream &os) const
+{
+ switch(context()->source().type()) {
+ case PM_CONTEXT_LOCAL:
+ os << "@:";
+ break;
+ case PM_CONTEXT_HOST:
+ os << context()->source().source() << ':';
+ break;
+ case PM_CONTEXT_ARCHIVE:
+ os << context()->source().source() << '/';
+ break;
+ }
+}
+
+void
+QmcMetric::dumpValue(QTextStream &stream, uint inst) const
+{
+ if (error(inst) < 0)
+ stream << pmErrStr(error(inst));
+ else if (!real())
+ stream << stringValue(inst);
+ else if (!event())
+ stream << value(inst) << " " << desc().units();
+}
+
+void
+QmcMetric::dump(QTextStream &stream, bool srcFlag, uint instance) const
+{
+ if (event())
+ dumpEventMetric(stream, srcFlag, instance);
+ else
+ dumpSampledMetric(stream, srcFlag, instance);
+}
+
+void
+QmcMetric::dumpSampledMetric(QTextStream &stream, bool srcFlag, uint instance) const
+{
+ Q_ASSERT(!event());
+
+ stream << name();
+
+ if (srcFlag == true)
+ dumpSource(stream);
+
+ if (my.status < 0)
+ stream << ": " << pmErrStr(my.status) << endl;
+ else if (hasInstances()) {
+ if (instance == UINT_MAX) {
+ if (numInst() == 1)
+ stream << ": 1 instance";
+ else
+ stream << ": " << numInst() << " instances";
+ if (indom()->changed())
+ stream << " (indom has changed)";
+ stream << endl;
+
+ for (int i = 0; i < numInst(); i++) {
+ stream << " [" << instID(i) << " or \"" << instName(i)
+ << "\" (" << my.values[i].instance() << ")] = ";
+ dumpValue(stream, i);
+ stream << endl;
+ }
+ }
+ else {
+ stream << '[' << instID(instance) << " or \"" << instName(instance)
+ << "\" (" << my.values[instance].instance() << ")] = ";
+ dumpValue(stream, instance);
+ stream << endl;
+ }
+ }
+ else {
+ stream << " = ";
+ dumpValue(stream, 0);
+ stream << endl;
+ }
+}
+
+QTextStream&
+operator<<(QTextStream &stream, const QmcMetric &metric)
+{
+ metric.dumpSource(stream);
+ stream << metric.name();
+ if (metric.numInst()) {
+ stream << "[\"" << metric.instName(0);
+ for (int i = 1; i < metric.numValues(); i++)
+ stream << "\", \"" << metric.instName(i);
+ stream << "\"]";
+ }
+ return stream;
+}
+
+void
+QmcMetric::setScaleUnits(pmUnits const& units)
+{
+ QmcContext *context = my.group->context(my.contextIndex);
+ QmcDesc &desc = context->desc(my.pmid);
+ desc.setScaleUnits(units);
+}
+
+int
+QmcMetric::update()
+{
+ uint i, err = 0;
+ uint num = numValues();
+ int sts;
+ pmAtomValue ival, oval;
+ double delta = context()->timeDelta();
+ static int wrap = -1;
+
+ if (num == 0 || my.status < 0)
+ return my.status;
+
+ // PCP_COUNTER_WRAP in environment enables "counter wrap" logic
+ if (wrap == -1)
+ wrap = (getenv("PCP_COUNTER_WRAP") != NULL);
+
+ for (i = 0; i < num; i++) {
+ my.values[i].setError(my.values[i].currentError());
+ if (my.values[i].error() < 0)
+ err++;
+ if (pmDebug & DBG_TRACE_VALUE) {
+ QTextStream cerr(stderr);
+ if (my.values[i].error() < 0)
+ cerr << "QmcMetric::update: " << spec(true, true, i)
+ << ": " << pmErrStr(my.values[i].error()) << endl;
+ }
+ }
+
+ if (!real())
+ return err;
+
+ if (desc().desc().sem == PM_SEM_COUNTER) {
+ for (i = 0; i < num; i++) {
+ QmcMetricValue& value = my.values[i];
+
+ if (value.error() < 0) { // we already know we
+ value.setValue(0.0); // don't have this value
+ continue;
+ }
+ if (value.previousError() < 0) { // we need two values
+ value.setValue(0.0); // for a rate
+ value.setError(value.previousError());
+ err++;
+ if (pmDebug & DBG_TRACE_VALUE) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::update: Previous: "
+ << spec(true, true, i) << ": "
+ << pmErrStr(value.error()) << endl;
+ }
+ continue;
+ }
+
+ value.setValue(value.currentValue() - value.previousValue());
+
+ // wrapped going forwards
+ if (value.value() < 0 && delta > 0) {
+ if (wrap) {
+ switch(desc().desc().type) {
+ case PM_TYPE_32:
+ case PM_TYPE_U32:
+ value.addValue((double)UINT_MAX+1);
+ break;
+ case PM_TYPE_64:
+ case PM_TYPE_U64:
+ value.addValue((double)ULONGLONG_MAX+1);
+ break;
+ }
+ }
+ else { // counter not monotonic
+ value.setValue(0.0); // increasing
+ value.setError(PM_ERR_VALUE);
+ err++;
+ continue;
+ }
+ }
+ // wrapped going backwards
+ else if (value.value() > 0 && delta < 0) {
+ if (wrap) {
+ switch(desc().desc().type) {
+ case PM_TYPE_32:
+ case PM_TYPE_U32:
+ value.subValue((double)UINT_MAX+1);
+ break;
+ case PM_TYPE_64:
+ case PM_TYPE_U64:
+ value.subValue((double)ULONGLONG_MAX+1);
+ break;
+ }
+ }
+ else { // counter not monotonic
+ value.setValue(0.0); // increasing
+ value.setError(PM_ERR_VALUE);
+ err++;
+ continue;
+ }
+ }
+
+ if (delta != 0) // sign of delta and v
+ value.divValue(delta); // should be the same
+ else
+ value.setValue(0.0); // nothing can have happened
+ }
+ }
+ else {
+ for (i = 0; i < num; i++) {
+ QmcMetricValue& value = my.values[i];
+ if (value.error() < 0)
+ value.setValue(0.0);
+ else
+ value.setValue(value.currentValue());
+ }
+ }
+
+ if (my.scale != 0.0) {
+ for (i = 0; i < num; i++) {
+ if (my.values[i].error() >= 0)
+ my.values[i].divValue(my.scale);
+ }
+ }
+
+ if (desc().useScaleUnits()) {
+ for (i = 0; i < num; i++) {
+ if (my.values[i].error() < 0)
+ continue;
+ ival.d = my.values[i].value();
+ pmUnits units = desc().desc().units;
+ sts = pmConvScale(PM_TYPE_DOUBLE, &ival, &units,
+ &oval, (pmUnits *)&(desc().scaleUnits()));
+ if (sts < 0)
+ my.values[i].setError(sts);
+ else {
+ my.values[i].setValue(oval.d);
+ if (pmDebug & DBG_TRACE_VALUE) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::update: scaled " << my.name
+ << " from " << ival.d << " to " << oval.d
+ << endl;
+ }
+ }
+ }
+ }
+
+ return err;
+}
+
+void
+QmcMetric::dumpAll() const
+{
+ QTextStream cerr(stderr);
+ cerr << *this << " from " << context()->source().desc()
+ << " with scale = " << my.scale << " and units = " << desc().units()
+ << endl;
+}
+
+void
+QmcMetric::dumpErr() const
+{
+ pmprintf("%s: Error: %s: %s\n", pmProgname,
+ (const char *)spec(true).toAscii(), pmErrStr(my.status));
+}
+
+// Instance list may not be valid, so pass inst as a string rather than
+// as an index
+
+void
+QmcMetric::dumpErr(const char *inst) const
+{
+ pmprintf("%s: Error: %s[%s]: %s\n", pmProgname,
+ (const char *)spec(true).toAscii(), inst, pmErrStr(my.status));
+}
+
+const char *
+QmcMetric::formatNumber(double value)
+{
+ static char buf[8];
+
+ if (value >= 0.0) {
+ if (value > 99950000000000.0)
+ strcpy(buf, " inf?");
+ else if (value > 99950000000.0)
+ sprintf(buf, "%5.2fT", value / 1000000000000.0);
+ else if (value > 99950000.0)
+ sprintf(buf, "%5.2fG", value / 1000000000.0);
+ else if (value > 99950.0)
+ sprintf(buf, "%5.2fM", value / 1000000.0);
+ else if (value > 99.95)
+ sprintf(buf, "%5.2fK", value / 1000.0);
+ else if (value > 0.005)
+ sprintf(buf, "%5.2f ", value);
+ else
+ strcpy(buf, " 0.00 ");
+ }
+ else {
+ if (value < -9995000000000.0)
+ strcpy(buf, " -inf?");
+ else if (value < -9995000000.0)
+ sprintf(buf, "%.2fT", value / 1000000000000.0);
+ else if (value < -9995000.0)
+ sprintf(buf, "%.2fG", value / 1000000000.0);
+ else if (value < -9995.0)
+ sprintf(buf, "%.2fM", value / 1000000.0);
+ else if (value < -9.995)
+ sprintf(buf, "%.2fK", value / 1000.0);
+ else if (value < -0.005)
+ sprintf(buf, "%.2f ", value);
+ else
+ strcpy(buf, " 0.00 ");
+ }
+
+ return buf;
+}
+
+void
+QmcMetric::shiftValues()
+{
+ for (int i = 0; i < my.values.size(); i++)
+ my.values[i].shiftValues();
+}
+
+void
+QmcMetric::setError(int sts)
+{
+ for (int i = 0; i < numValues(); i++) {
+ QmcMetricValue &value = my.values[i];
+ value.setCurrentError(sts);
+ }
+}
+
+void
+QmcMetric::extractValues(pmValueSet const* set)
+{
+ int i, j, index, inst;
+ pmValue const *value = NULL;
+ bool found;
+ QmcIndom *indomPtr = indom();
+
+ Q_ASSERT(set->pmid == desc().id());
+
+ if (set->numval > 0) {
+ if (hasIndom()) {
+ // If the number of instances are not the expected number
+ // then mark the indom as changed
+ if (!my.explicitInst && (my.values.size() != set->numval)) {
+ if (pmDebug & DBG_TRACE_INDOM) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::extractValues: implicit indom "
+ << pmInDomStr(indomPtr->id()) << " changed ("
+ << set->numval << " != " << my.values.size() << ')'
+ << endl;
+ }
+ indomPtr->hasChanged();
+ updateIndom();
+ }
+
+ for (i = 0; i < numInst(); i++) {
+ QmcMetricValue &valueRef = my.values[i];
+ inst = my.values[i].instance();
+ index = indomPtr->index(inst);
+ found = false;
+
+ // If the index is within range, try it first
+ if (index >= 0 && index < set->numval) {
+ value = &(set->vlist[index]);
+
+ // Found it in the same spot as last time
+ if (value->inst == indomPtr->inst(inst))
+ found = true;
+ }
+
+ // Search for it from the top
+ for (j = 0; found == false && j < set->numval; j++) {
+ if (set->vlist[j].inst == indomPtr->inst(inst)) {
+ index = j;
+ value = &(set->vlist[j]);
+ indomPtr->setIndex(inst, j);
+ found = true;
+ }
+ }
+
+ if (found) {
+ if (real())
+ extractNumericMetric(set, value, valueRef);
+ else if (!event())
+ extractArrayMetric(set, value, valueRef);
+ else
+ extractEventMetric(set, index, valueRef);
+ }
+ else { // Cannot find it
+ if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::extractValues: "
+ << spec(true, true, i) << ": "
+ << pmErrStr(PM_ERR_VALUE) << endl;
+ }
+
+ if (valueRef.previousError() != PM_ERR_VALUE)
+ indomPtr->hasChanged();
+
+ valueRef.setCurrentError(PM_ERR_VALUE);
+ }
+ }
+ }
+ else if (set->numval == 1) {
+ // We have no instances at this point in time
+ if (my.values.size() == 0 && hasInstances())
+ indomPtr->hasChanged();
+ else {
+ QmcMetricValue &valueRef = my.values[0];
+ value = &(set->vlist[0]);
+
+ if (real())
+ extractNumericMetric(set, value, valueRef);
+ else if (!event())
+ extractArrayMetric(set, value, valueRef);
+ else
+ extractEventMetric(set, 0, valueRef);
+ }
+ }
+ else { // Did not expect any instances
+ if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::extractValues: " << spec(true)
+ << " is a singular metric but result contained "
+ << set->numval << " values" << endl;
+ }
+ setError(PM_ERR_VALUE);
+ }
+ }
+ else if (set->numval == 0) {
+ if (!(hasInstances() && numInst() == 0)) {
+ if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::extractValues: numval == 0: "
+ << spec(true, false) << ": " << pmErrStr(PM_ERR_VALUE)
+ << endl;
+ }
+ setError(PM_ERR_VALUE);
+ if (hasInstances())
+ indomPtr->hasChanged();
+ }
+ }
+ else {
+ if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::extractValues: numval < 0: "
+ << spec(true, false)
+ << ": " << pmErrStr(set->numval) << endl;
+ }
+ setError(set->numval);
+ if (hasInstances())
+ indomPtr->hasChanged();
+ }
+}
+
+/*
+ * Display-able aggregate (for diagnostics) - up to
+ * first 16 characters displayed. Uses a similar
+ * approach to that taken in pmPrintValue().
+ */
+void
+QmcMetric::aggregateAsString(pmValue const *vp, char *buffer, int buflen)
+{
+ char *p = &vp->value.pval->vbuf[0];
+
+ memset(buffer, '.', buflen);
+ for (int i = 0; i < (buflen/2)-1; i++, p++) {
+ if (i < vp->value.pval->vlen - PM_VAL_HDR_SIZE)
+ sprintf(buffer + (i*2), "%02x", *p & 0xff);
+ }
+ buffer[buflen-1] = '\0';
+}
+
+void
+QmcMetric::extractArrayMetric(pmValueSet const *set, pmValue const *vp, QmcMetricValue &valueRef)
+{
+ pmAtomValue result;
+ int sts, type = desc().desc().type;
+
+ if (aggregate(type)) {
+ char buffer[32];
+ aggregateAsString(vp, buffer, sizeof(buffer));
+ valueRef.setStringValue(buffer);
+ }
+ else if ((sts = pmExtractValue(set->valfmt, vp,
+ type, &result, PM_TYPE_STRING)) >= 0) {
+ valueRef.setStringValue(result.cp);
+ if (result.cp)
+ free(result.cp);
+ }
+ else {
+ valueRef.setCurrentError(sts);
+ }
+}
+
+void
+QmcMetric::extractNumericMetric(pmValueSet const *set, pmValue const *value, QmcMetricValue &valueRef)
+{
+ pmAtomValue result;
+ int sts;
+
+ if ((sts = pmExtractValue(set->valfmt, value,
+ desc().desc().type, &result, PM_TYPE_DOUBLE)) >= 0)
+ valueRef.setCurrentValue(result.d);
+ else
+ valueRef.setCurrentError(sts);
+}
+
+void
+QmcMetricValue::extractEventRecords(QmcContext *context, int recordCount, pmResult **result)
+{
+ pmID parameterID;
+ pmID missedID = QmcEventRecord::eventMissed();
+ pmID flagsID = QmcEventRecord::eventFlags();
+ int i, p, r, parameterCount;
+
+ my.eventRecords.resize(recordCount);
+
+ for (r = 0; r < recordCount; r++) {
+ QmcEventRecord &record = my.eventRecords[r];
+
+ // count is the size of my.parameter vector (less flags/missed)
+ parameterCount = 0;
+ for (i = 0; i < result[r]->numpmid; i++) {
+ parameterID = result[r]->vset[i]->pmid;
+ if (parameterID != flagsID && parameterID != missedID)
+ parameterCount++;
+ }
+
+ // initialise this record
+ record.setTimestamp(&result[r]->timestamp);
+ record.setParameterCount(parameterCount);
+ record.setMissed(0);
+ record.setFlags(0);
+
+ // i indexes into result[r], p indexes into my.parameter vector
+ for (i = p = 0; i < result[r]->numpmid; i++) {
+ pmValueSet *valueSet = result[r]->vset[i];
+ parameterID = valueSet->pmid;
+ if (parameterID == flagsID)
+ record.setFlags(valueSet->vlist[0].value.lval);
+ else if (parameterID == missedID)
+ record.setMissed(valueSet->vlist[0].value.lval);
+ else
+ record.setParameter(p++, parameterID, context, valueSet);
+ }
+
+ if (pmDebug & DBG_TRACE_VALUE) {
+ QTextStream cerr(stderr);
+ pmValueSet *valueSet = result[r]->vset[0];
+ record.dump(cerr, valueSet->vlist[0].inst, r);
+ }
+ }
+}
+
+void
+QmcMetric::extractEventMetric(pmValueSet const *valueSet, int index, QmcMetricValue &valueRef)
+{
+ pmValueSet *values = (pmValueSet *)valueSet;
+ pmResult **result;
+ int sts;
+
+ if ((sts = pmUnpackEventRecords(values, index, &result)) >= 0) {
+ valueRef.extractEventRecords(context(), sts, result);
+ pmFreeEventResult(result);
+ }
+ else {
+ valueRef.setCurrentError(sts);
+ }
+}
+
+int
+QmcEventRecord::setParameter(int index, pmID pmid, QmcContext *context, pmValueSet const *vsp)
+{
+ QString *name;
+ QmcDesc *desc;
+ QmcIndom *indom;
+ int sts, type;
+
+ if (vsp->numval <= 0) // no value or an error
+ return vsp->numval;
+
+ if ((sts = context->lookup(pmid, &name, &desc, &indom)) < 0)
+ return sts;
+
+ QmcEventParameter &parameter = my.parameters[index];
+ parameter.setPMID(pmid);
+ parameter.setNamePtr(name);
+ parameter.setDescPtr(desc);
+ parameter.setIndomPtr(indom);
+ parameter.setValueCount(vsp->numval);
+
+ type = desc->desc().type;
+ for (int i = 0; i < vsp->numval; i++) {
+ pmAtomValue result;
+ const pmValue *vp = &vsp->vlist[i];
+ QmcMetricValue *value = parameter.valuePtr(i);
+
+ sts = PM_ERR_TYPE; // no nesting events
+ if (QmcMetric::real(type) == true) {
+ if ((sts = pmExtractValue(vsp->valfmt, vp,
+ type, &result, PM_TYPE_DOUBLE)) >= 0)
+ value->setCurrentValue(result.d);
+ } else if (QmcMetric::aggregate(type) == true) {
+ char buffer[32];
+ QmcMetric::aggregateAsString(vp, buffer, sizeof(buffer));
+ value->setStringValue(buffer);
+ } else if (QmcMetric::event(type) == false) {
+ if ((sts = pmExtractValue(vsp->valfmt, vp,
+ type, &result, PM_TYPE_STRING)) >= 0) {
+ value->setStringValue(result.cp);
+ free(result.cp);
+ }
+ }
+ value->setInstance(vp->inst);
+ if (sts < 0)
+ value->setCurrentError(sts);
+ }
+ return 0;
+}
+
+QString
+QmcEventRecord::parameterAsString(int index) const
+{
+ QString identifier;
+
+ if (index >= my.parameters.size())
+ return QString::null;
+
+ int type = my.parameters[index].type();
+ if (QmcMetric::real(type))
+ return QString::number(my.parameters.at(index).value(0));
+ if (QmcMetric::event(type))
+ return QString::null;
+ return my.parameters.at(index).stringValue(0);
+}
+
+QString
+QmcEventRecord::identifier() const
+{
+ //
+ // If the ID flag is set, the identifier is always the
+ // first parameter.
+ //
+ if (my.flags & PM_EVENT_FLAG_ID)
+ return parameterAsString(0);
+ return QString::null;
+}
+
+QString
+QmcEventRecord::parent() const
+{
+ //
+ // If PARENT flag is set, then parent identifier is either
+ // the first parameter (if no ID, bit wierd) or the second
+ // (thats expected typical usage anyway).
+ //
+ if (my.flags & PM_EVENT_FLAG_PARENT)
+ return parameterAsString((my.flags & PM_EVENT_FLAG_ID) != 0);
+ return QString::null;
+}
+
+void
+QmcEventRecord::parameterSummary(QString &os, int instID) const
+{
+ for (int i = 0; i < my.parameters.size(); i++)
+ my.parameters.at(i).summary(os, instID);
+}
+
+void
+QmcEventParameter::summary(QString &os, int instID) const
+{
+ pmDesc desc = my.desc->desc();
+
+ for (int i = 0; i < my.values.size(); i++) {
+ QmcMetricValue const &value = my.values.at(i);
+
+ os.append(" ").append(*my.name);
+ if (desc.indom != PM_INDOM_NULL) {
+ if (my.values.size() > 1)
+ os.append("\n").append(" ");
+ QString name = my.indom->name(instID);
+ if (name == QString::null)
+ os.append("[").append(instID).append("]");
+ else
+ os.append("[\"").append(name).append("\"]");
+ }
+ os.append(" ");
+
+ if (QmcMetric::real(desc.type))
+ os.append(QString::number(value.currentValue()));
+ else if (QmcMetric::aggregate(desc.type))
+ os.append("[").append(value.stringValue()).append("]");
+ else if (QmcMetric::event(desc.type) == false)
+ os.append("\"").append(value.stringValue()).append("\"");
+ os.append("\n");
+ }
+}
+
+void
+QmcEventParameter::setValueCount(int numInst)
+{
+ my.values.resize(numInst);
+}
+
+QmcMetricValue *
+QmcEventParameter::valuePtr(int inst)
+{
+ return &my.values[inst];
+}
+
+int
+QmcEventParameter::type() const
+{
+ return my.desc->desc().type;
+}
+
+double
+QmcEventParameter::value(int inst) const
+{
+ return my.values[inst].currentValue();
+}
+
+QString
+QmcEventParameter::stringValue(int inst) const
+{
+ return my.values[inst].stringValue();
+}
+
+void
+QmcEventParameter::dump(QTextStream &os, int instID) const
+{
+ pmDesc desc = my.desc->desc();
+
+ for (int i = 0; i < my.values.size(); i++) {
+ QmcMetricValue const &value = my.values.at(i);
+
+ os << " " << *my.name;
+ if (desc.indom != PM_INDOM_NULL) {
+ if (my.values.size() > 1)
+ os << endl << " ";
+ QString name = my.indom->name(instID);
+ if (name == QString::null)
+ os << "[" << instID << "]";
+ else
+ os << "[\"" << name << "\"]";
+ }
+ os << " ";
+
+ if (QmcMetric::real(desc.type))
+ os << value.currentValue();
+ else if (QmcMetric::aggregate(desc.type))
+ os << "[" << value.stringValue() << "]";
+ else if (QmcMetric::event(desc.type) == false)
+ os << "\"" << value.stringValue() << "\"";
+ os << endl;
+ }
+}
+
+void
+QmcEventRecord::dump(QTextStream &os, int instID, uint recordID) const
+{
+ os << " " << QmcSource::timeStringBrief(&my.timestamp);
+ os << " --- event record [" << recordID << "]";
+ if (my.flags) {
+ os.setIntegerBase(16);
+ os << " flags 0x" << (uint)my.flags << " (" << pmEventFlagsStr(my.flags) << ")";
+ os.setIntegerBase(10);
+ }
+ os << " ---" << endl;
+ if (my.flags & PM_EVENT_FLAG_MISSED)
+ os << " ==> " << my.missed << " missed event records" << endl;
+ for (int i = 0; i < my.parameters.size(); i++)
+ my.parameters.at(i).dump(os, instID);
+}
+
+void
+QmcMetricValue::dumpEventRecords(QTextStream &os, int instID) const
+{
+ os << my.eventRecords.size() << " event records" << endl;
+ for (int i = 0; i < my.eventRecords.size(); i++)
+ my.eventRecords.at(i).dump(os, instID, i);
+}
+
+void
+QmcMetric::dumpEventMetric(QTextStream &os, bool srcFlag, uint instance) const
+{
+ Q_ASSERT(event());
+
+ for (int i = 0; i < my.values.size(); i++) {
+ int instID;
+
+ if (srcFlag == true)
+ dumpSource(os);
+ os << name();
+
+ instID = PM_IN_NULL;
+ if (hasInstances()) {
+ if (instance != UINT_MAX)
+ instID = (int)instance;
+ else
+ instID = my.values[i].instance();
+
+ QString inst = instName(instID);
+ if (inst == QString::null)
+ os << "[" << instID << "]";
+ else
+ os << "[\"" << inst << "\"]";
+ }
+ os << ": ";
+ my.values[i].dumpEventRecords(os, instID);
+ }
+}
+
+pmID
+QmcEventRecord::eventMissed(void)
+{
+ static pmID eventMissed = PM_IN_NULL;
+ static const char *nameMissed[] = { "event.missed" };
+
+ if (eventMissed == PM_ID_NULL)
+ if (pmLookupName(1, (char **)nameMissed, &eventMissed) < 0)
+ eventMissed = PM_ID_NULL;
+ return eventMissed;
+}
+
+pmID
+QmcEventRecord::eventFlags(void)
+{
+ static pmID eventFlags = PM_IN_NULL;
+ static const char *nameFlags[] = { "event.flags" };
+
+ if (eventFlags == PM_ID_NULL)
+ if (pmLookupName(1, (char **)nameFlags, &eventFlags) < 0)
+ eventFlags = PM_ID_NULL;
+ return eventFlags;
+}
+
+bool
+QmcMetric::updateIndom(void)
+{
+ int i = 0, j, oldNum = numInst(), newNum, newInst;
+ QmcIndom *indomPtr = indom();
+
+ if (status() < 0 || !hasIndom())
+ return false;
+
+ if (indomPtr->changed())
+ indomPtr->update();
+
+ my.explicitInst = false;
+
+ newNum = my.active ? indomPtr->numActiveInsts() : indomPtr->numInsts();
+
+ // If the number of instances are the same then we know that no
+ // modifications to the metric instance list is required as these
+ // instances are all referenced in the indom
+ //
+ // If the instance list is only active instances, then we need to
+ // check all the instances as the number may be the same
+ //
+ if (newNum == oldNum) {
+ if (my.active) {
+ for (i = 0; i < my.values.size(); i++)
+ if (!indomPtr->activeInst(my.values[i].instance()))
+ break;
+ }
+ if (!my.active || i == my.values.size()) {
+ if (pmDebug & DBG_TRACE_INDOM) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::updateIndom: No change required" << endl;
+ }
+ return false;
+ }
+ }
+
+ // Duplicate the current values
+ // Replace the old index into the indom instance list with the
+ // internal instance identifiers so that these can be correlated
+ // if the order of instances changes
+ QList<QmcMetricValue> oldValues = my.values;
+ for (i = 0; i < oldNum; i++) {
+ oldValues[i].setInstance(indomPtr->inst(my.values[i].instance()));
+ indomPtr->removeRef(my.values[i].instance());
+ }
+
+ setupValues(newNum);
+ indomPtr->refAll(my.active);
+
+ if (my.active) {
+ for (i = 0, j = 0; i < indomPtr->listLen(); i++)
+ if (!indomPtr->nullInst(i) && indomPtr->activeInst(i))
+ my.values[j++].setInstance(i);
+ }
+ else {
+ for (i = 0, j = 0; i < indomPtr->listLen(); i++)
+ if (!indomPtr->nullInst(i))
+ my.values[j++].setInstance(i);
+ }
+
+ // Copy values of instances that have not gone away
+ // Note that their position may have changed
+ for (i = 0; i < my.values.size(); i++) {
+ if (i < oldValues.size() &&
+ indomPtr->inst(my.values[i].instance()) ==
+ oldValues[i].instance()) {
+ newInst = my.values[i].instance();
+ my.values[i] = oldValues[i];
+ my.values[i].setInstance(newInst);
+ continue;
+ }
+ for (j = 0; j < oldValues.size(); j++)
+ if (indomPtr->inst(my.values[i].instance()) ==
+ oldValues[j].instance()) {
+ newInst = my.values[i].instance();
+ my.values[i] = oldValues[j];
+ my.values[i].setInstance(newInst);
+ break;
+ }
+
+ // Need to set all error flags to avoid problems with rate conversion
+ if (j == oldValues.size())
+ my.values[i].setAllErrors(PM_ERR_VALUE);
+ }
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcMetric::updateIndom: " << spec(true) << ": Had "
+ << oldNum << " instances, now have " << numInst() << endl;
+ }
+
+ indomPtr->update();
+
+ return true;
+}
+
+int
+QmcMetric::addInst(QString const& name)
+{
+ if (my.status < 0)
+ return my.status;
+
+ if (!hasInstances())
+ return PM_ERR_INDOM;
+
+ int i = indom()->lookup(name);
+ if (i >= 0) {
+ setupValues(my.values.size() + 1);
+ my.values.last().setInstance(i);
+ }
+
+ return i;
+}
+
+void
+QmcMetric::removeInst(uint index)
+{
+ Q_ASSERT(hasInstances());
+ indom()->removeRef(my.values[index].instance());
+ my.values.removeAt(index);
+}