summaryrefslogtreecommitdiff
path: root/src/libpcp_qmc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp_qmc')
-rw-r--r--src/libpcp_qmc/GNUmakefile27
-rw-r--r--src/libpcp_qmc/src/GNUmakefile24
-rw-r--r--src/libpcp_qmc/src/libpcp_qmc.pro13
-rw-r--r--src/libpcp_qmc/src/qmc.h26
-rw-r--r--src/libpcp_qmc/src/qmc_context.cpp402
-rw-r--r--src/libpcp_qmc/src/qmc_context.h115
-rw-r--r--src/libpcp_qmc/src/qmc_desc.cpp203
-rw-r--r--src/libpcp_qmc/src/qmc_desc.h49
-rw-r--r--src/libpcp_qmc/src/qmc_group.cpp521
-rw-r--r--src/libpcp_qmc/src/qmc_group.h103
-rw-r--r--src/libpcp_qmc/src/qmc_indom.cpp481
-rw-r--r--src/libpcp_qmc/src/qmc_indom.h137
-rw-r--r--src/libpcp_qmc/src/qmc_metric.cpp1248
-rw-r--r--src/libpcp_qmc/src/qmc_metric.h336
-rw-r--r--src/libpcp_qmc/src/qmc_source.cpp425
-rw-r--r--src/libpcp_qmc/src/qmc_source.h108
-rw-r--r--src/libpcp_qmc/src/qmc_time.cpp174
-rw-r--r--src/libpcp_qmc/src/qmc_time.h121
18 files changed, 4513 insertions, 0 deletions
diff --git a/src/libpcp_qmc/GNUmakefile b/src/libpcp_qmc/GNUmakefile
new file mode 100644
index 0000000..552118e
--- /dev/null
+++ b/src/libpcp_qmc/GNUmakefile
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2000,2004 Silicon Graphics, Inc. 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.
+#
+# This library 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 Lesser General Public
+# License for more details.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/src/include/builddefs
+
+SUBDIRS = src
+
+default install : $(SUBDIRS)
+ $(SUBDIRS_MAKERULE)
+
+include $(BUILDRULES)
+
+default_pcp : default
+
+install_pcp : install
diff --git a/src/libpcp_qmc/src/GNUmakefile b/src/libpcp_qmc/src/GNUmakefile
new file mode 100644
index 0000000..3c8d8f8
--- /dev/null
+++ b/src/libpcp_qmc/src/GNUmakefile
@@ -0,0 +1,24 @@
+TOPDIR = ../../..
+LIBRARY = libpcp_qmc
+PROJECT = $(LIBRARY).pro
+include $(TOPDIR)/src/include/builddefs
+
+HEADERS = $(shell echo *.h)
+SOURCES = $(shell echo *.cpp)
+
+default: build-me
+
+include $(BUILDRULES)
+
+ifeq "$(ENABLE_QT)" "true"
+build-me: $(PROJECT)
+ $(QTMAKE)
+else
+build-me:
+endif
+
+install: default
+
+default_pcp: default
+
+install_pcp: install
diff --git a/src/libpcp_qmc/src/libpcp_qmc.pro b/src/libpcp_qmc/src/libpcp_qmc.pro
new file mode 100644
index 0000000..2be408f
--- /dev/null
+++ b/src/libpcp_qmc/src/libpcp_qmc.pro
@@ -0,0 +1,13 @@
+TARGET = pcp_qmc
+TEMPLATE = lib
+VERSION = 1.0.0
+CONFIG += qt staticlib warn_on
+INCLUDEPATH += ../../include
+
+HEADERS = qmc_context.h qmc_desc.h qmc_group.h \
+ qmc_indom.h qmc_metric.h qmc_source.h \
+ qmc_time.h
+
+SOURCES = qmc_context.cpp qmc_desc.cpp qmc_group.cpp \
+ qmc_indom.cpp qmc_metric.cpp qmc_source.cpp \
+ qmc_time.cpp
diff --git a/src/libpcp_qmc/src/qmc.h b/src/libpcp_qmc/src/qmc.h
new file mode 100644
index 0000000..cdd0f99
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 1998-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.
+ */
+#ifndef QMC_H
+#define QMC_H
+
+#include <pcp/pmapi.h>
+#include <pcp/impl.h>
+
+//
+// Classes
+//
+class QmcContext;
+class QmcDesc;
+class QmcGroup;
+class QmcIndom;
+class QmcMetric;
+class QmcSource;
+
+#endif // QMC_H
diff --git a/src/libpcp_qmc/src/qmc_context.cpp b/src/libpcp_qmc/src/qmc_context.cpp
new file mode 100644
index 0000000..0a45a61
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_context.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2012 Red Hat.
+ * Copyright (c) 2007-2008 Aconex. All Rights Reserved.
+ * Copyright (c) 1997,2005 Silicon Graphics, Inc. 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 "qmc_context.h"
+#include "qmc_metric.h"
+#include <limits.h>
+#include <QVector>
+#include <QStringList>
+#include <QHashIterator>
+
+QStringList *QmcContext::theStringList;
+
+QmcContext::QmcContext(QmcSource* source)
+{
+ my.delta = 0.0;
+ my.context = -1;
+ my.source = source;
+ my.needReconnect = false;
+
+ if (my.source->status() >= 0)
+ my.context = my.source->dupContext();
+ else
+ my.context = my.source->status();
+}
+
+QmcContext::~QmcContext()
+{
+ while (my.metrics.isEmpty() == false) {
+ delete my.metrics.takeFirst();
+ }
+ QHashIterator<pmID, QmcDesc*> descs(my.descCache);
+ while (descs.hasNext()) {
+ descs.next();
+ delete descs.value();
+ }
+ while (my.indoms.isEmpty() == false) {
+ delete my.indoms.takeFirst();
+ }
+ if (my.context >= 0)
+ my.source->delContext(my.context);
+}
+
+int
+QmcContext::lookupName(pmID pmid, QString **name)
+{
+ char *value;
+ int sts = 0;
+
+ if ((sts = pmUseContext(my.context)) < 0)
+ return sts;
+
+ if (my.pmidCache.contains(pmid) == false) {
+ if ((sts = pmNameID(pmid, &value)) >= 0) {
+ *name = new QString(value);
+ my.pmidCache.insert(pmid, *name);
+ free(value);
+ }
+ } else {
+ QString *np = my.pmidCache.value(pmid);
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::lookupName: Matched id "
+ << pmIDStr(pmid) << " to \"" << *np << "\"" << endl;
+ }
+ *name = np;
+ }
+ return sts;
+}
+
+int
+QmcContext::lookupPMID(const char *name, pmID& id)
+{
+ QString key = name;
+ int sts;
+
+ if ((sts = pmUseContext(my.context)) < 0)
+ return sts;
+
+ if (my.nameCache.contains(key) == false) {
+ if ((sts = pmLookupName(1, (char **)(&name), &id)) >= 0)
+ my.nameCache.insert(key, id);
+ } else {
+ id = my.nameCache.value(key);
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::lookupPMID: Matched \"" << name
+ << "\" to id " << pmIDStr(id) << endl;
+ }
+ sts = 1;
+ }
+ return sts;
+}
+
+int
+QmcContext::lookupInDom(const char *name, uint_t& indom)
+{
+ pmID pmid;
+ int sts = lookupPMID(name, pmid);
+ if (sts < 0)
+ return sts;
+ return lookupInDom(pmid, indom);
+}
+
+int
+QmcContext::lookupDesc(pmID pmid, QmcDesc **descriptor)
+{
+ int sts;
+ QmcDesc *descPtr;
+
+ if ((sts = pmUseContext(my.context)) < 0)
+ return sts;
+
+ if (my.descCache.contains(pmid) == false) {
+ descPtr = new QmcDesc(pmid);
+ if (descPtr->status() < 0) {
+ sts = descPtr->status();
+ delete descPtr;
+ return sts;
+ }
+ my.descCache.insert(pmid, descPtr);
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::lookupDesc: Add descriptor for "
+ << pmIDStr(descPtr->id()) << endl;
+ }
+ }
+ else {
+ descPtr = my.descCache.value(pmid);
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::lookupDesc: Reusing descriptor "
+ << pmIDStr(descPtr->id()) << endl;
+ }
+ }
+ *descriptor = descPtr;
+ return 0;
+}
+
+int
+QmcContext::lookup(pmID pmid, QString **namePtr, QmcDesc **descPtr, QmcIndom **indomPtr)
+{
+ uint_t indom;
+ int sts;
+
+ if ((sts = lookupName(pmid, namePtr)) < 0)
+ return sts;
+ if ((sts = lookupDesc(pmid, descPtr)) < 0)
+ return sts;
+ if ((sts = lookupInDom(pmid, indom)) < 0)
+ return sts;
+ *indomPtr = (indom == UINT_MAX) ? NULL : my.indoms[indom];
+ return 0;
+}
+
+int
+QmcContext::lookupInDom(pmID pmid, uint_t& indom)
+{
+ QmcDesc *descPtr;
+ int sts;
+
+ if ((sts = lookupDesc(pmid, &descPtr)) < 0)
+ return sts;
+ return lookupInDom(descPtr, indom);
+}
+
+int
+QmcContext::lookupInDom(QmcDesc *descPtr, uint_t& indom)
+{
+ int i, sts;
+ QmcIndom *indomPtr;
+
+ if ((sts = pmUseContext(my.context)) < 0)
+ return sts;
+
+ indom = UINT_MAX;
+ if (descPtr->desc().indom != PM_INDOM_NULL) {
+ for (i = 0; i < my.indoms.size(); i++)
+ if (my.indoms[i]->id() == (int)descPtr->desc().indom)
+ break;
+ if (i == my.indoms.size()) {
+ indomPtr = new QmcIndom(my.source->type(), *descPtr);
+ if (indomPtr->status() < 0) {
+ sts = indomPtr->status();
+ delete indomPtr;
+ return sts;
+ }
+ my.indoms.append(indomPtr);
+ indom = my.indoms.size() - 1;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::lookupInDom: Add indom for "
+ << pmInDomStr(indomPtr->id()) << endl;
+ }
+ }
+ else {
+ indomPtr = my.indoms[i];
+ indom = i;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::lookupInDom: Reusing indom "
+ << pmInDomStr(indomPtr->id()) << endl;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+QmcContext::useTZ()
+{
+ if (my.source->tzHandle() >= 0)
+ return pmUseZone(my.source->tzHandle());
+ return 0;
+}
+
+QTextStream&
+operator<<(QTextStream &stream, const QmcContext &context)
+{
+ stream << context.source().desc() << " has "
+ << context.numMetrics() << " metrics";
+ return stream;
+}
+
+void
+QmcContext::dump(QTextStream &stream)
+{
+ stream << "Context " << my.context << " has " << my.nameCache.size()
+ << " metric names for source:" << endl;
+ my.source->dump(stream);
+}
+
+void
+QmcContext::dumpMetrics(QTextStream &stream)
+{
+ for (int i = 0; i < my.metrics.size(); i++)
+ stream << " [" << i << "] "
+ << my.metrics[i]->spec(false, true) << endl;
+}
+
+void
+QmcContext::addMetric(QmcMetric *metric)
+{
+ pmID pmid;
+ int i;
+
+ my.metrics.append(metric);
+ if (metric->status() >= 0) {
+ pmid = metric->desc().desc().pmid;
+ for (i = 0; i < my.pmids.size(); i++)
+ if (my.pmids[i] == pmid)
+ break;
+ if (i == my.pmids.size())
+ my.pmids.append(pmid);
+ metric->setIdIndex(i);
+ }
+}
+
+int
+QmcContext::fetch(bool update)
+{
+ int i, sts;
+ pmResult *result;
+
+ for (i = 0; i < my.metrics.size(); i++) {
+ QmcMetric *metric = my.metrics[i];
+ if (metric->status() < 0)
+ continue;
+ metric->shiftValues();
+ }
+
+ // Inform each indom that we are about to do a new fetch so any
+ // indom changes are now irrelevant
+ for (i = 0; i < my.indoms.size(); i++)
+ my.indoms[i]->newFetch();
+
+ sts = pmUseContext(my.context);
+ if (sts >= 0) {
+ for (i = 0; i < my.indoms.size(); i++) {
+ if (my.indoms[i]->diffProfile())
+ sts = my.indoms[i]->genProfile();
+ }
+ }
+ else if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::fetch: Unable to switch to this context: "
+ << pmErrStr(sts) << endl;
+ }
+
+ if (sts >= 0 && my.needReconnect) {
+ sts = pmReconnectContext(my.context);
+ if (sts >= 0) {
+ my.needReconnect = false;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::fetch: Reconnected context \""
+ << *my.source << endl;
+ }
+ }
+ else if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::fetch: Reconnect failed: "
+ << pmErrStr(sts) << endl;
+ }
+ }
+
+ if (sts >= 0 && my.pmids.size()) {
+ if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::fetch: fetching context " << *this << endl;
+ }
+
+ sts = pmFetch(my.pmids.size(),
+ (pmID *)(my.pmids.toVector().data()), &result);
+ if (sts >= 0) {
+ my.previousTime = my.currentTime;
+ my.currentTime = result->timestamp;
+ my.delta = __pmtimevalSub(&my.currentTime, &my.previousTime);
+ for (i = 0; i < my.metrics.size(); i++) {
+ QmcMetric *metric = my.metrics[i];
+ if (metric->status() < 0)
+ continue;
+ Q_ASSERT((int)metric->idIndex() < result->numpmid);
+ metric->extractValues(result->vset[metric->idIndex()]);
+ }
+ pmFreeResult(result);
+ }
+ else {
+ if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::fetch: pmFetch: " << pmErrStr(sts) << endl;
+ }
+ for (i = 0; i < my.metrics.size(); i++) {
+ QmcMetric *metric = my.metrics[i];
+ if (metric->status() < 0)
+ continue;
+ metric->setError(sts);
+ }
+ if (sts == PM_ERR_IPC || sts == PM_ERR_TIMEOUT)
+ my.needReconnect = true;
+ }
+
+ if (update) {
+ if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::fetch: Updating metrics" << endl;
+ }
+ for (i = 0; i < my.metrics.size(); i++) {
+ QmcMetric *metric = my.metrics[i];
+ if (metric->status() < 0)
+ continue;
+ metric->update();
+ }
+ }
+ }
+ else if (pmDebug & DBG_TRACE_OPTFETCH) {
+ QTextStream cerr(stderr);
+ cerr << "QmcContext::fetch: nothing to fetch" << endl;
+ }
+
+ return sts;
+}
+
+void
+QmcContext::dometric(const char *name)
+{
+ theStringList->append(name);
+}
+
+int
+QmcContext::traverse(const char *name, QStringList &list)
+{
+ int sts;
+
+ theStringList = &list;
+ theStringList->clear();
+
+ if ((sts = pmUseContext(my.context)) < 0)
+ return sts;
+
+ sts = pmTraversePMNS(name, QmcContext::dometric);
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ if (sts >= 0) {
+ cerr << "QmcContext::traverse: Found " << list.size()
+ << " names from " << name << endl;
+ }
+ else
+ cerr << "QmcContext::traverse: Failed: " << pmErrStr(sts)
+ << endl;
+ }
+
+ return sts;
+}
diff --git a/src/libpcp_qmc/src/qmc_context.h b/src/libpcp_qmc/src/qmc_context.h
new file mode 100644
index 0000000..8751597
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_context.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2012 Red Hat.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ * Copyright (c) 1998-2005 Silicon Graphics, Inc. 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.
+ */
+#ifndef QMC_CONTEXT_H
+#define QMC_CONTEXT_H
+
+#include "qmc.h"
+#include "qmc_desc.h"
+#include "qmc_indom.h"
+#include "qmc_source.h"
+
+#include <qhash.h>
+#include <qlist.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+class QmcContext
+{
+public:
+ QmcContext(QmcSource *source);
+ ~QmcContext();
+
+ int status() const // Is this a valid context?
+ { return (my.context < 0) ? my.context : 0; }
+
+ int handle() const // The PMAPI context handle
+ { return my.context; }
+
+ QmcSource const& source() const // Source description
+ { return *my.source; }
+
+ unsigned int numIDs() const // Number of unique pmIDs in use
+ { return my.pmids.size(); }
+
+ pmID id(unsigned int index) const // Access to each unique pmID
+ { return my.pmids[index]; }
+
+ QmcDesc const& desc(pmID pmid) const
+ { return *(my.descCache.value(pmid)); }
+ QmcDesc& desc(pmID pmid) // Access to each descriptor
+ { return *(my.descCache.value(pmid)); }
+
+ unsigned int numIndoms() const // Number of indom descriptors
+ { return my.indoms.size(); } // requested from this context
+
+ QmcIndom const& indom(unsigned int index) const
+ { return *(my.indoms[index]); }
+ QmcIndom& indom(unsigned int index) // Access to each indom
+ { return *(my.indoms[index]); }
+
+ // Lookup the pmid or indom (implies descriptor) for metric <name>|<id>
+ int lookupPMID(const char *name, pmID& id);
+ int lookupInDom(const char *name, unsigned int& indom);
+ int lookupInDom(QmcDesc *desc, uint_t& indom);
+
+ // Lookup various structures using the pmid
+ int lookupInDom(pmID pmid, unsigned int& indom);
+ int lookupName(pmID pmid, QString **name);
+ int lookupDesc(pmID pmid, QmcDesc **desc);
+ int lookup(pmID pmid, QString **name, QmcDesc **desc, QmcIndom **indom);
+
+ int useTZ(); // Use this timezone
+
+ unsigned int numMetrics() const // Number of metrics using this context
+ { return my.metrics.size(); }
+
+ QmcMetric const& metric(unsigned int index) const
+ { return *(my.metrics[index]); }
+ QmcMetric& metric(unsigned int index) // Get a handle to a metric
+ { return *(my.metrics[index]); }
+
+ void addMetric(QmcMetric* metric); // Add a metric using this context
+
+ int fetch(bool update); // Fetch metrics using this context
+
+ struct timeval const& timeStamp() const
+ { return my.currentTime; }
+
+ double timeDelta() const // Time since previous fetch
+ { return my.delta; }
+
+ int traverse(const char *name, QStringList &list); // Walk the namespace
+
+ friend QTextStream &operator<<(QTextStream &stream, const QmcContext &rhs);
+ void dump(QTextStream &stream); // Dump debugging information
+ void dumpMetrics(QTextStream &stream); // Dump list of metrics
+
+private:
+ struct {
+ int context; // PMAPI Context handle
+ bool needReconnect; // Need to reconnect the context
+ QmcSource *source; // Handle to the source description
+ QHash<QString, pmID> nameCache; // Reverse map from names to PMIDs
+ QHash<pmID, QString*> pmidCache;// Mapping between PMIDs and names
+ QHash<pmID, QmcDesc*> descCache;// Mapping between PMIDs and descs
+ QList<pmID> pmids; // List of valid PMIDs to be fetched
+ QList<QmcIndom*> indoms; // List of requested indoms
+ QList<QmcMetric*> metrics; // List of metrics using this context
+ struct timeval currentTime; // Time of current fetch
+ struct timeval previousTime; // Time of previous fetch
+ double delta; // Time between fetches
+ } my;
+
+ static QStringList *theStringList; // List of metric names in traversal
+ static void dometric(const char *);
+};
+
+#endif // QMC_CONTEXT_H
diff --git a/src/libpcp_qmc/src/qmc_desc.cpp b/src/libpcp_qmc/src/qmc_desc.cpp
new file mode 100644
index 0000000..af496d8
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_desc.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 1998,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 "qmc_desc.h"
+#include <QTextStream>
+
+QmcDesc::QmcDesc(pmID pmid)
+{
+ my.pmid = pmid;
+ my.scaleFlag = false;
+ my.status = pmLookupDesc(my.pmid, &my.desc);
+ if (my.status >= 0) {
+ my.scaleUnits = my.desc.units;
+ setUnitStrings();
+ }
+ else if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcDesc::QmcDesc: unable to lookup "
+ << pmIDStr(my.pmid) << ": " << pmErrStr(my.status) << endl;
+ }
+}
+
+void
+QmcDesc::setUnitStrings()
+{
+ const char *units = pmUnitsStr(&my.scaleUnits);
+ const char *shortUnits = shortUnitsString(&my.scaleUnits);
+
+ if (my.desc.sem == PM_SEM_COUNTER) {
+ if (my.scaleFlag &&
+ my.scaleUnits.dimTime == 1 &&
+ my.scaleUnits.dimSpace == 0 &&
+ my.scaleUnits.dimCount == 0) {
+ my.units = "Time Utilization";
+ my.shortUnits = "util";
+ }
+ else {
+ my.units = units;
+ my.units.append(" / second");
+ my.shortUnits = shortUnits;
+ my.shortUnits.append("/s");
+ }
+ }
+ else {
+ if (units[0] == '\0')
+ my.units = "none";
+ else
+ my.units = units;
+ if (shortUnits[0] == '\0')
+ my.shortUnits = "none";
+ else
+ my.shortUnits = shortUnits;
+ }
+}
+
+void
+QmcDesc::setScaleUnits(const pmUnits &units)
+{
+ my.scaleUnits = units;
+ my.scaleFlag = true;
+ setUnitStrings();
+}
+
+const char *
+QmcDesc::shortUnitsString(pmUnits *pu)
+{
+ const char *spaceString, *timeString, *countString;
+ char sbuf[20], tbuf[20], cbuf[20];
+ static char buf[60];
+ char *p;
+
+ spaceString = timeString = countString = NULL;
+ buf[0] = '\0';
+
+ if (pu->dimSpace) {
+ switch (pu->scaleSpace) {
+ case PM_SPACE_BYTE:
+ spaceString = "b";
+ break;
+ case PM_SPACE_KBYTE:
+ spaceString = "Kb";
+ break;
+ case PM_SPACE_MBYTE:
+ spaceString = "Mb";
+ break;
+ case PM_SPACE_GBYTE:
+ spaceString = "Gb";
+ break;
+ case PM_SPACE_TBYTE:
+ spaceString = "Tb";
+ break;
+ default:
+ sprintf(sbuf, "space-%d", pu->scaleSpace);
+ spaceString = sbuf;
+ break;
+ }
+ }
+ if (pu->dimTime) {
+ switch (pu->scaleTime) {
+ case PM_TIME_NSEC:
+ timeString = "ns";
+ break;
+ case PM_TIME_USEC:
+ timeString = "us";
+ break;
+ case PM_TIME_MSEC:
+ timeString = "msec";
+ break;
+ case PM_TIME_SEC:
+ timeString = "s";
+ break;
+ case PM_TIME_MIN:
+ timeString = "m";
+ break;
+ case PM_TIME_HOUR:
+ timeString = "h";
+ break;
+ default:
+ sprintf(tbuf, "time-%d", pu->scaleTime);
+ timeString = tbuf;
+ break;
+ }
+ }
+ if (pu->dimCount) {
+ switch (pu->scaleCount) {
+ case 0:
+ countString = "c";
+ break;
+ case 1:
+ countString = "cx10";
+ break;
+ default:
+ sprintf(cbuf, "cx10^%d", pu->scaleCount);
+ countString = cbuf;
+ break;
+ }
+ }
+
+ p = buf;
+
+ if (pu->dimSpace > 0) {
+ if (pu->dimSpace == 1)
+ sprintf(p, "%s", spaceString);
+ else
+ sprintf(p, "%s^%d", spaceString, pu->dimSpace);
+ while (*p) p++;
+ }
+ if (pu->dimTime > 0) {
+ if (pu->dimTime == 1)
+ sprintf(p, "%s", timeString);
+ else
+ sprintf(p, "%s^%d", timeString, pu->dimTime);
+ while (*p) p++;
+ }
+ if (pu->dimCount > 0) {
+ if (pu->dimCount == 1)
+ sprintf(p, "%s", countString);
+ else
+ sprintf(p, "%s^%d", countString, pu->dimCount);
+ while (*p) p++;
+ }
+ if (pu->dimSpace < 0 || pu->dimTime < 0 || pu->dimCount < 0) {
+ *p++ = '/';
+ if (pu->dimSpace < 0) {
+ if (pu->dimSpace == -1)
+ sprintf(p, "%s", spaceString);
+ else
+ sprintf(p, "%s^%d", spaceString, -pu->dimSpace);
+ while (*p) p++;
+ }
+ if (pu->dimTime < 0) {
+ if (pu->dimTime == -1)
+ sprintf(p, "%s", timeString);
+ else
+ sprintf(p, "%s^%d", timeString, -pu->dimTime);
+ while (*p) p++;
+ }
+ if (pu->dimCount < 0) {
+ if (pu->dimCount == -1)
+ sprintf(p, "%s", countString);
+ else
+ sprintf(p, "%s^%d", countString, -pu->dimCount);
+ while (*p) p++;
+ }
+ }
+
+ if (buf[0] == '\0') {
+ if (pu->scaleCount == 1)
+ sprintf(buf, "x10");
+ else if (pu->scaleCount != 0)
+ sprintf(buf, "x10^%d", pu->scaleCount);
+ }
+ else
+ *p = '\0';
+
+ return buf;
+}
diff --git a/src/libpcp_qmc/src/qmc_desc.h b/src/libpcp_qmc/src/qmc_desc.h
new file mode 100644
index 0000000..1f269ff
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_desc.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1998-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.
+ */
+#ifndef QMC_DESC_H
+#define QMC_DESC_H
+
+#include "qmc.h"
+#include <qstring.h>
+
+class QmcDesc
+{
+public:
+ QmcDesc(pmID pmid);
+
+ int status() const { return my.status; }
+ pmID id() const { return my.pmid; }
+ pmDesc desc() const { return my.desc; }
+ const pmDesc *descPtr() const { return &my.desc; }
+ const QString units() const { return my.units; }
+ const QString shortUnits() const { return my.shortUnits; }
+ const pmUnits &scaleUnits() const { return my.scaleUnits; }
+
+ void setScaleUnits(const pmUnits &units);
+
+ // Are we using scaled units provided by a call to setScaleUnits?
+ bool useScaleUnits() const { return my.scaleFlag; }
+
+private:
+ struct {
+ int status;
+ pmID pmid;
+ pmDesc desc;
+ QString units;
+ QString shortUnits;
+ pmUnits scaleUnits;
+ bool scaleFlag;
+ } my;
+
+ void setUnitStrings();
+ static const char *shortUnitsString(pmUnits *pu);
+};
+
+#endif // QMC_DESC_H
diff --git a/src/libpcp_qmc/src/qmc_group.cpp b/src/libpcp_qmc/src/qmc_group.cpp
new file mode 100644
index 0000000..d2ccb72
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_group.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2013, Red Hat.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ * Copyright (c) 1997-2005 Silicon Graphics, Inc. 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 <limits.h>
+#include <float.h>
+#include <math.h>
+
+#include <pcp/pmapi.h>
+#include <pcp/impl.h>
+#include "qmc_group.h"
+#include "qmc_source.h"
+#include "qmc_context.h"
+#include "qmc_metric.h"
+
+int QmcGroup::tzLocal = -1;
+bool QmcGroup::tzLocalInit = false;
+QString QmcGroup::tzLocalString;
+QString QmcGroup::localHost;
+
+QmcGroup::QmcGroup(bool restrictArchives)
+{
+ my.restrictArchives = restrictArchives;
+ my.mode = PM_CONTEXT_HOST;
+ my.use = -1;
+ my.localSource = 0;
+ my.tzFlag = unknownTZ;
+ my.tzDefault = -1;
+ my.tzUser = -1;
+ my.tzGroupIndex = 0;
+ my.timeEndReal = 0.0;
+
+ // Get timezone from environment
+ if (tzLocalInit == false) {
+ char buf[MAXHOSTNAMELEN];
+ gethostname(buf, MAXHOSTNAMELEN);
+ buf[MAXHOSTNAMELEN-1] = '\0';
+ localHost = buf;
+
+ char *tz = __pmTimezone();
+ if (tz == NULL)
+ pmprintf("%s: Warning: Unable to get timezone from environment\n",
+ pmProgname);
+ else {
+ tzLocal = pmNewZone(tz);
+ if (tzLocal < 0)
+ pmprintf("%s: Warning: Timezone for localhost: %s\n",
+ pmProgname, pmErrStr(tzLocal));
+ else {
+ tzLocalString = tz;
+ my.tzDefault = tzLocal;
+ my.tzFlag = localTZ;
+ }
+ }
+ tzLocalInit = true;
+ }
+}
+
+QmcGroup::~QmcGroup()
+{
+ for (int i = 0; i < my.contexts.size(); i++)
+ if (my.contexts[i])
+ delete my.contexts[i];
+}
+
+int
+QmcGroup::use(int type, const QString &theSource, int flags)
+{
+ int sts = 0;
+ unsigned int i;
+ QString source(theSource);
+
+ if (type == PM_CONTEXT_LOCAL) {
+ for (i = 0; i < numContexts(); i++)
+ if (my.contexts[i]->source().type() == type)
+ break;
+ }
+ else if (type == PM_CONTEXT_ARCHIVE) {
+ if (source == QString::null) {
+ pmprintf("%s: Error: Archive context requires archive path\n",
+ pmProgname);
+ return PM_ERR_NOCONTEXT;
+ }
+ // This doesn't take into account {.N,.meta,.index,} ... but
+ // then again, nor does pmNewContext. More work ... useful?
+ for (i = 0; i < numContexts(); i++) {
+ if (source == my.contexts[i]->source().source())
+ break;
+ }
+ }
+ else {
+ if (source == QString::null) {
+ if (!defaultDefined()) {
+ createLocalContext();
+ if (!defaultDefined()) {
+ pmprintf("%s: Error: "
+ "Cannot connect to PMCD on localhost: %s\n",
+ pmProgname,
+ pmErrStr(my.localSource->status()));
+ return my.localSource->status();
+ }
+ }
+ source = my.contexts[0]->source().source();
+ }
+
+ for (i = 0; i < numContexts(); i++) {
+ if (source == my.contexts[i]->source().source())
+ break;
+ }
+ }
+
+ if (i == numContexts()) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: No direct match for context \"" << source
+ << "\" (type " << type << ")." << endl;
+ }
+
+ // Determine live or archive mode by the first source
+ if (i == 0)
+ my.mode = type;
+
+ // If the assumed mode differs from the requested context type
+ // we may need to map the host to an archive
+ if (my.mode != type) {
+
+ if (my.mode == PM_CONTEXT_HOST && type == PM_CONTEXT_ARCHIVE) {
+ pmprintf("%s: Error: Archive \"%s\" requested "
+ "after live mode was assumed.\n", pmProgname,
+ (const char *)source.toAscii());
+ return PM_ERR_NOCONTEXT;
+ }
+
+ // If we are in archive mode, map hosts to archives of same host
+ if (my.mode == PM_CONTEXT_ARCHIVE && type == PM_CONTEXT_HOST) {
+ QString chop1 = source.remove(PM_LOG_MAXHOSTLEN-1, INT_MAX);
+ for (i = 0; i < numContexts(); i++) {
+ QString chop2 = my.contexts[i]->source().host();
+ chop2.remove(PM_LOG_MAXHOSTLEN-1, INT_MAX);
+ if (chop1 == chop2)
+ break;
+ }
+
+ if (i == numContexts()) {
+ pmprintf("%s: Error: No archives were specified "
+ "for host \"%s\"\n", pmProgname,
+ (const char *)source.toAscii());
+ return PM_ERR_NOTARCHIVE;
+ }
+ }
+ }
+ }
+
+ if (i == numContexts()) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Creating new context for \"" << source
+ << '\"' << endl;
+ }
+
+ QmcSource *src = QmcSource::getSource(type, source, flags, false);
+ if (src == NULL) {
+ pmprintf("%s: Error: No archives were specified for host \"%s\"\n",
+ pmProgname, (const char *)source.toAscii());
+ return PM_ERR_NOTARCHIVE;
+ }
+
+ QmcContext *newContext = new QmcContext(src);
+ if (newContext->handle() < 0) {
+ sts = newContext->handle();
+ pmprintf("%s: Error: %s: %s\n", pmProgname,
+ (const char *)source.toAscii(), pmErrStr(sts));
+ delete newContext;
+ return sts;
+ }
+
+ // If we are in archive mode and are adding an archive,
+ // make sure another archive for the same host does not exist
+ if (my.restrictArchives && type == PM_CONTEXT_ARCHIVE) {
+ for (i = 0; i < numContexts(); i++)
+ // No need to restrict comparison here, both are from
+ // log labels.
+ if (my.contexts[i]->source().host() ==
+ newContext->source().host()) {
+ pmprintf("%s: Error: Archives \"%s\" and \"%s\" are from "
+ "the same host \"%s\"\n", pmProgname,
+ my.contexts[i]->source().sourceAscii(),
+ newContext->source().sourceAscii(),
+ my.contexts[i]->source().hostAscii());
+ delete newContext;
+ return PM_ERR_NOCONTEXT;
+ }
+ }
+
+ my.contexts.append(newContext);
+ my.use = my.contexts.size() - 1;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Added context " << my.use << ": "
+ << *newContext << endl;
+ }
+ }
+
+ // We found a match, do we need to use a different context?
+ else if (i != (unsigned int)my.use) {
+ my.use = i;
+ sts = useContext();
+ if (sts < 0) {
+ pmprintf("%s: Error: Unable to use context to %s: %s\n", pmProgname,
+ context()->source().sourceAscii(), pmErrStr(sts));
+ return sts;
+ }
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Using existing context " << my.use
+ << " for " << context()->source().desc() << endl;
+ }
+ }
+ else if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::use: Using current context " << my.use
+ << " (handle = " << context()->handle() << ") for "
+ << context()->source().desc() << endl;
+ }
+
+ return context()->handle();
+}
+
+int
+QmcGroup::useTZ()
+{
+ int sts = context()->useTZ();
+
+ if (sts >= 0) {
+ my.tzDefault = context()->source().tzHandle();
+ my.tzFlag = groupTZ;
+ my.tzGroupIndex = my.use;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::useTZ: Using timezone of "
+ << context()->source().desc()
+ << " (" << my.tzGroupIndex << ')' << endl;
+ }
+ }
+ return sts;
+}
+
+int
+QmcGroup::useTZ(const QString &tz)
+{
+ int sts = pmNewZone(tz.toAscii());
+
+ if (sts >= 0) {
+ my.tzUser = sts;
+ my.tzUserString = tz;
+ my.tzFlag = userTZ;
+ my.tzDefault = sts;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::useTZ: Switching timezones to \"" << tz
+ << "\" (" << my.tzUserString << ')' << endl;
+ }
+ }
+ return sts;
+}
+
+int
+QmcGroup::useLocalTZ()
+{
+ if (tzLocal >= 0) {
+ int sts = pmUseZone(tzLocal);
+ if (sts > 0) {
+ my.tzFlag = localTZ;
+ my.tzDefault = tzLocal;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::useTZ: Using timezone of host \"localhost\""
+ << endl;
+ }
+ }
+ return sts;
+ }
+ return tzLocal;
+}
+
+void
+QmcGroup::defaultTZ(QString &label, QString &tz)
+{
+ if (my.tzFlag == userTZ) {
+ label = my.tzUserString;
+ tz = my.tzUserString;
+ }
+ else if (my.tzFlag == localTZ) {
+ label = localHost;
+ tz = tzLocalString;
+ }
+ else {
+ label = my.contexts[my.tzGroupIndex]->source().host();
+ tz = my.contexts[my.tzGroupIndex]->source().timezone();
+ }
+}
+
+int
+QmcGroup::useDefaultTZ()
+{
+ if (my.tzFlag == unknownTZ)
+ return -1;
+ return pmUseZone(my.tzDefault);
+}
+
+int
+QmcGroup::useDefault()
+{
+ if (numContexts() == 0)
+ createLocalContext();
+ if (numContexts() == 0)
+ return my.localSource->status();
+ my.use = 0;
+ return pmUseContext(context()->handle());
+}
+
+void
+QmcGroup::createLocalContext()
+{
+ if (numContexts() == 0) {
+ QTextStream cerr(stderr);
+ QmcSource *localSource = QmcSource::getSource(PM_CONTEXT_HOST,
+ localHost, 0, false);
+ if (localSource->status() < 0 && pmDebug & DBG_TRACE_PMC)
+ cerr << "QmcGroup::createLocalContext: Default context to "
+ << localSource->desc() << " failed: "
+ << pmErrStr(localSource->status()) << endl;
+ else if (pmDebug & DBG_TRACE_PMC)
+ cerr << "QmcGroup::createLocalContext: Default context to "
+ << localSource->desc() << endl;
+
+ QmcContext *newContext = new QmcContext(localSource);
+ if (newContext->handle() < 0) {
+ pmprintf("%s: Error: %s: %s\n", pmProgname,
+ (const char *)localHost.toAscii(), pmErrStr(newContext->handle()));
+ }
+ my.contexts.append(newContext);
+ my.use = my.contexts.size() - 1;
+ }
+}
+
+void
+QmcGroup::updateBounds()
+{
+ double newStart = DBL_MAX;
+ double newEnd = 0.0;
+ double startReal;
+ double endReal;
+ struct timeval startTv;
+ struct timeval endTv;
+
+ my.timeStart.tv_sec = 0;
+ my.timeStart.tv_usec = 0;
+ my.timeEnd = my.timeStart;
+
+ for (unsigned int i = 0; i < numContexts(); i++) {
+ if (my.contexts[i]->handle() >= 0 &&
+ my.contexts[i]->source().type() == PM_CONTEXT_ARCHIVE) {
+ startTv = my.contexts[i]->source().start();
+ endTv = my.contexts[i]->source().end();
+ startReal = __pmtimevalToReal(&startTv);
+ endReal = __pmtimevalToReal(&endTv);
+ if (startReal < newStart)
+ newStart = startReal;
+ if (endReal > newEnd)
+ newEnd = endReal;
+ }
+ }
+
+ __pmtimevalFromReal(newStart, &my.timeStart);
+ __pmtimevalFromReal(newEnd, &my.timeEnd);
+ my.timeEndReal = newEnd;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::updateBounds: start = " << my.timeStart.tv_sec
+ << '.' << my.timeStart.tv_usec << ", end = "
+ << my.timeEnd.tv_sec << '.' << my.timeEnd.tv_usec << endl;
+ }
+}
+
+void
+QmcGroup::dump(QTextStream &stream)
+{
+ stream << "mode: ";
+ switch(my.mode) {
+ case PM_CONTEXT_LOCAL:
+ stream << "local";
+ break;
+ case PM_CONTEXT_HOST:
+ stream << "live host";
+ break;
+ case PM_CONTEXT_ARCHIVE:
+ stream << "archive";
+ break;
+ }
+
+ stream << ", timezone: ";
+ switch(my.tzFlag) {
+ case QmcGroup::localTZ:
+ stream << "local = \"" << tzLocalString;
+ break;
+ case QmcGroup::userTZ:
+ stream << "user = \"" << my.tzUserString;
+ break;
+ case QmcGroup::groupTZ:
+ stream << "group = \""
+ << my.contexts[my.tzGroupIndex]->source().timezone();
+ break;
+ case QmcGroup::unknownTZ:
+ stream << "unknown = \"???";
+ break;
+ }
+ stream << "\": " << endl;
+
+ stream << " " << numContexts() << " contexts:" << endl;
+ for (unsigned int i = 0; i < numContexts(); i++) {
+ stream << " [" << i << "] " << *(my.contexts[i]) << endl;
+ my.contexts[i]->dumpMetrics(stream);
+ }
+}
+
+int
+QmcGroup::useContext()
+{
+ int sts = 0;
+
+ if ((context()->status() == 0) &&
+ (sts = pmUseContext(context()->handle())) < 0)
+ pmprintf("%s: Error: Unable to reuse context to %s: %s\n",
+ pmProgname, context()->source().sourceAscii(), pmErrStr(sts));
+ return sts;
+}
+
+QmcMetric *
+QmcGroup::addMetric(char const *string, double theScale, bool active)
+{
+ QmcMetric *metric = new QmcMetric(this, string, theScale, active);
+ if (metric->status() >= 0)
+ metric->context()->addMetric(metric);
+ return metric;
+}
+
+QmcMetric *
+QmcGroup::addMetric(pmMetricSpec *theMetric, double theScale, bool active)
+{
+ QmcMetric *metric = new QmcMetric(this, theMetric, theScale, active);
+ if (metric->status() >= 0)
+ metric->context()->addMetric(metric);
+ return metric;
+}
+
+int
+QmcGroup::fetch(bool update)
+{
+ int sts = 0;
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::fetch: " << numContexts() << " contexts" << endl;
+ }
+
+ for (unsigned int i = 0; i < numContexts(); i++)
+ my.contexts[i]->fetch(update);
+
+ if (numContexts())
+ sts = useContext();
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcGroup::fetch: Done" << endl;
+ }
+
+ return sts;
+}
+
+int
+QmcGroup::setArchiveMode(int mode, const struct timeval *when, int interval)
+{
+ int sts, result = 0;
+
+ for (unsigned int i = 0; i < numContexts(); i++) {
+ if (my.contexts[i]->source().type() != PM_CONTEXT_ARCHIVE)
+ continue;
+
+ sts = pmUseContext(my.contexts[i]->handle());
+ if (sts < 0) {
+ pmprintf("%s: Error: Unable to switch to context for %s: %s\n",
+ pmProgname, my.contexts[i]->source().sourceAscii(),
+ pmErrStr(sts));
+ result = sts;
+ continue;
+ }
+ sts = pmSetMode(mode, when, interval);
+ if (sts < 0) {
+ pmprintf("%s: Error: Unable to set context mode for %s: %s\n",
+ pmProgname, my.contexts[i]->source().sourceAscii(),
+ pmErrStr(sts));
+ result = sts;
+ }
+ }
+ sts = useContext();
+ if (sts < 0)
+ result = sts;
+ return result;
+}
diff --git a/src/libpcp_qmc/src/qmc_group.h b/src/libpcp_qmc/src/qmc_group.h
new file mode 100644
index 0000000..05edadf
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_group.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013, Red Hat.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ * Copyright (c) 1998-2005 Silicon Graphics, Inc. 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.
+ */
+#ifndef QMC_GROUP_H
+#define QMC_GROUP_H
+
+#include "qmc.h"
+#include "qmc_context.h"
+
+#include <qlist.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+class QmcGroup
+{
+public:
+ enum TimeZoneFlag { localTZ, userTZ, groupTZ, unknownTZ };
+
+public:
+ QmcGroup(bool restrictArchives = false);
+ ~QmcGroup();
+
+ int mode() const { return my.mode; }
+
+ unsigned int numContexts() const { return my.contexts.size(); }
+
+ // Return a handle to the contexts
+ QmcContext* context() const { return my.contexts[my.use]; }
+ QmcContext* context(unsigned int index) const { return my.contexts[index]; }
+
+ // Index to the active context
+ unsigned int contextIndex() const { return my.use; }
+
+ int use(int type, const QString &source, int flags = 0);
+ int use(unsigned int index) { my.use = index; return useContext(); }
+ bool defaultDefined() const { return (numContexts() > 0); }
+ int useDefault();
+
+ void createLocalContext();
+
+ // Add a new metric to the group
+ QmcMetric* addMetric(char const* str, double theScale = 0.0,
+ bool active = false);
+ QmcMetric* addMetric(pmMetricSpec* theMetric, double theScale = 0.0,
+ bool active = false);
+
+ // Fetch all the metrics in this group
+ // By default, do all rate conversions and counter wraps
+ int fetch(bool update = true);
+
+ // Set the archive position and mode
+ int setArchiveMode(int mode, const struct timeval *when, int interval);
+
+ int useTZ(); // Use TZ of current context as default
+ int useTZ(const QString &tz); // Use this TZ as default
+ int useLocalTZ(); // Use local TZ as default
+ void defaultTZ(QString &label, QString &tz);
+
+ TimeZoneFlag defaultTZ() const { return my.tzFlag; }
+ int useDefaultTZ();
+
+ struct timeval const& logStart() const { return my.timeStart; }
+ struct timeval const& logEnd() const { return my.timeEnd; }
+ void updateBounds(); // Determine the archive start and finish times
+
+ void dump(QTextStream &os);
+
+private:
+ struct {
+ QList<QmcContext*> contexts; // List of all contexts in this group
+ bool restrictArchives; // Only one archive per host
+ int mode; // Default context type
+ int use; // Context in use
+ QmcSource *localSource; // Localhost source desc
+
+ TimeZoneFlag tzFlag; // default TZ type
+ int tzDefault; // handle to default TZ
+ int tzUser; // handle to user defined TZ
+ QString tzUserString; // user defined TZ;
+ int tzGroupIndex; // index to group context used for
+ // current timezone
+ struct timeval timeStart; // Start of first archive
+ struct timeval timeEnd; // End of last archive
+ double timeEndReal; // End of last archive
+ } my;
+
+ // Timezone for localhost from environment
+ static bool tzLocalInit; // got TZ from environment
+ static int tzLocal; // handle to environment TZ
+ static QString tzLocalString; // environment TZ string
+ static QString localHost; // name of localhost
+
+ int useContext();
+};
+
+#endif // QMC_GROUP_H
diff --git a/src/libpcp_qmc/src/qmc_indom.cpp b/src/libpcp_qmc/src/qmc_indom.cpp
new file mode 100644
index 0000000..67dd260
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_indom.cpp
@@ -0,0 +1,481 @@
+/*
+ * 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.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+
+#include "qmc_indom.h"
+#include "qmc_desc.h"
+#include <ctype.h>
+#include <QVector>
+#include <QStringList>
+
+QmcInstance::QmcInstance()
+{
+ my.inst = PM_IN_NULL;
+ my.refCount = 0;
+ my.index = -1;
+ my.active = false;
+}
+
+QmcInstance::QmcInstance(int id, const char* name)
+{
+ my.inst = id;
+ my.name = name;
+ my.refCount = 0;
+ my.index = -1;
+ my.active = true;
+}
+
+void
+QmcInstance::deactivate(int nullIndex)
+{
+ my.inst = PM_IN_NULL;
+ my.name = "";
+ my.refCount = 0;
+ my.index = nullIndex;
+ my.active = false;
+}
+
+QmcInstance const&
+QmcInstance::operator=(QmcInstance const& rhs)
+{
+ if (this != &rhs) {
+ my.inst = rhs.inst();
+ my.name = rhs.name();
+ my.refCount = rhs.refCount();
+ my.index = rhs.index();
+ my.active = rhs.active();
+ }
+ return *this;
+}
+
+QmcIndom::QmcIndom(int type, QmcDesc &desc)
+{
+ int *instList;
+ char **nameList;
+
+ my.type = type;
+ my.id = desc.desc().indom;
+ my.profile = false;
+ my.changed = false;
+ my.updated = true;
+ my.count = 0;
+ my.nullCount = 0;
+ my.nullIndex = UINT_MAX;
+ my.numActive = 0;
+ my.numActiveRef = 0;
+
+ if (my.id == PM_INDOM_NULL)
+ my.status = PM_ERR_INDOM;
+ else if (my.type == PM_CONTEXT_HOST || my.type == PM_CONTEXT_LOCAL)
+ my.status = pmGetInDom(my.id, &instList, &nameList);
+ else if (my.type == PM_CONTEXT_ARCHIVE)
+ my.status = pmGetInDomArchive(my.id, &instList, &nameList);
+ else
+ my.status = PM_ERR_NOCONTEXT;
+
+ if (my.status > 0) {
+ for (int i = 0; i < my.status; i++)
+ my.instances.append(QmcInstance(instList[i], nameList[i]));
+ my.numActive = my.status;
+ free(instList);
+ free(nameList);
+
+ if (pmDebug & DBG_TRACE_INDOM) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::QmcIndom: indom ";
+ }
+ }
+ else if (my.status < 0 && pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::QmcIndom: unable to lookup "
+ << pmInDomStr(my.id) << " from "
+ << (my.type == PM_CONTEXT_ARCHIVE ? "archive" : "host/local")
+ << " source: " << pmErrStr(my.status) << endl;
+ }
+}
+
+int
+QmcIndom::lookup(QString const &name)
+{
+ int i;
+ bool ok;
+ QStringList list;
+
+ for (i = 0; i < my.instances.size(); i++) {
+ if (my.instances[i].null())
+ continue;
+ if (my.instances[i].name().compare(name) == 0) {
+ if (my.instances[i].refCount() == 0) {
+ my.profile = true;
+ my.count++;
+ if (my.instances[i].active())
+ my.numActiveRef++;
+ }
+ my.instances[i].refCountInc();
+ return i;
+ }
+ }
+
+ // Match up to the first space
+ // Need this for proc and similiar agents
+
+ for (i = 0; i < my.instances.size(); i++) {
+ if (my.instances[i].null())
+ continue;
+ list = my.instances[i].name().split(QChar(' '));
+ if (list.size() <= 1)
+ continue;
+ if (name.compare(list.at(0)) == 0) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::lookup: inst \"" << name << "\"(" << i
+ << ") matched to \"" << my.instances[i].name() << "\"("
+ << i << ')' << endl;
+ }
+ if (my.instances[i].refCount() == 0) {
+ my.profile = true;
+ my.count++;
+ if (my.instances[i].active())
+ my.numActiveRef++;
+ }
+ my.instances[i].refCountInc();
+ return i;
+ }
+ }
+
+ // If the instance requested is numeric, then ignore leading
+ // zeros in the instance up to the first space
+ int nameNumber = name.toInt(&ok);
+
+ // The requested instance is numeric
+ if (ok) {
+ for (i = 0; i < my.instances.size(); i++) {
+ if (my.instances[i].null())
+ continue;
+
+ list = my.instances[i].name().split(QChar(' '));
+ if (list.size() <= 1)
+ continue;
+ int instNumber = list.at(0).toInt(&ok);
+ if (!ok)
+ continue;
+ if (instNumber == nameNumber) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::lookup: numerical inst \""
+ << name << " matched to \"" << my.instances[i].name()
+ << "\"(" << i << ')' << endl;
+ }
+ if (my.instances[i].refCount() == 0) {
+ my.profile = true;
+ my.count++;
+ if (my.instances[i].active())
+ my.numActiveRef++;
+ }
+ my.instances[i].refCountInc();
+ return i;
+ }
+ }
+ }
+
+ return -1; // we don't know about that instance
+}
+
+void
+QmcIndom::refAll(bool active)
+{
+ my.numActiveRef = 0;
+
+ for (int i = 0; i < my.instances.size(); i++) {
+ if (my.instances[i].null() || (active && !my.instances[i].active()))
+ continue;
+
+ if (my.instances[i].refCount() == 0)
+ my.profile = true;
+ if (my.instances[i].active())
+ my.numActiveRef++;
+
+ my.instances[i].refCountInc();
+ }
+ my.count = my.instances.size() - my.nullCount;
+}
+
+void
+QmcIndom::removeRef(uint index)
+{
+ Q_ASSERT(my.instances[index].refCount());
+
+ my.instances[index].refCountDec();
+ if (my.instances[index].refCount() == 0) {
+ my.profile = true;
+ my.count--;
+ if (my.instances[index].active())
+ my.numActiveRef--;
+ }
+}
+
+int
+QmcIndom::genProfile()
+{
+ int i, j;
+ int sts = 0;
+ int *ptr = NULL;
+ QVector<int> list;
+ const char *action = NULL;
+
+ // If all instances are referenced or there are no instances
+ // then request all instances
+ if (my.numActiveRef == my.numActive || my.numActive == 0) {
+ sts = pmAddProfile(my.id, 0, NULL);
+ action = "ALL";
+ }
+ // If the number of referenced instances is less than the number
+ // of unreferenced active instances, then the smallest profile
+ // is to add all the referenced instances
+ else if (my.count < (my.numActive - my.numActiveRef)) {
+ action = "ADD";
+ sts = pmDelProfile(my.id, 0, NULL);
+ if (sts >= 0) {
+ list.resize(my.count);
+ for (i = 0, j = 0; i < my.instances.size(); i++)
+ if (!my.instances[i].null() && my.instances[i].refCount())
+ list[j++] = my.instances[i].inst();
+ ptr = list.data();
+ sts = pmAddProfile(my.id, list.size(), ptr);
+ }
+ }
+ // Delete those active instances that are not referenced
+ else {
+ action = "DELETE";
+ sts = pmAddProfile(my.id, 0, NULL);
+ if (sts >= 0) {
+ list.resize(my.instances.size() - my.count);
+ for (i = 0, j = 0; i < my.instances.size(); i++)
+ if (!my.instances[i].null() &&
+ my.instances[i].refCount() == 0 &&
+ my.instances[i].active())
+ list[j++] = my.instances[i].inst();
+ ptr = list.data();
+ sts = pmDelProfile(my.id, list.size(), ptr);
+ }
+ }
+
+ if (pmDebug & (DBG_TRACE_PMC | DBG_TRACE_INDOM | DBG_TRACE_PROFILE)) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::genProfile: id = " << my.id << ", count = "
+ << my.count << ", numInsts = " << numInsts() << ", active = "
+ << my.numActive << ", activeRef = " << my.numActiveRef
+ << ": " << action << " ptr = " << ptr;
+ if (sts < 0)
+ cerr << ", sts = " << sts << ": " << pmErrStr(sts);
+ cerr << endl;
+ }
+
+ if (sts >= 0)
+ my.profile = false;
+ return sts;
+}
+
+void
+QmcIndom::dump(QTextStream &os) const
+{
+ os << pmInDomStr(my.id) << ": " << numInsts() << " instances ("
+ << my.nullCount << " NULL)" << endl;
+ for (int i = 0; i < my.instances.size(); i++)
+ if (!my.instances[i].null())
+ os << " [" << my.instances[i].inst() << "] = \""
+ << my.instances[i].name() << "\" ("
+ << my.instances[i].refCount() << " refs) "
+ << (my.instances[i].active() ? "active" : "inactive") << endl;
+ else
+ os << " NULL -> " << my.instances[i].index() << endl;
+}
+
+int
+QmcIndom::update()
+{
+ int *instList;
+ char **nameList;
+ int i, j, count;
+ int oldLen = my.instances.size();
+ uint oldNullCount = my.nullCount;
+ int sts = 0;
+
+ // If the indom has already been updated, just check that all instances
+ // are referenced and remove any that have gone away.
+ if (!my.changed || my.updated) {
+ for (i = 0; i < oldLen; i++) {
+ QmcInstance &inst = my.instances[i];
+ if (inst.refCount() || inst.null() || inst.active())
+ continue;
+ inst.deactivate(my.nullIndex);
+ my.nullIndex = i;
+ my.nullCount++;
+ my.profile = true;
+ }
+ if (pmDebug & DBG_TRACE_INDOM && my.nullCount != oldNullCount) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::update: Cleaning indom " << pmInDomStr(my.id)
+ << ": Removed " << my.nullCount - oldNullCount
+ << " instances" << endl;
+ }
+ return 0;
+ }
+
+ my.updated = true;
+
+ if (my.type == PM_CONTEXT_ARCHIVE)
+ return 0;
+
+ if (my.type == PM_CONTEXT_HOST || my.type == PM_CONTEXT_LOCAL)
+ sts = pmGetInDom(my.id, &instList, &nameList);
+
+ my.numActive = 0;
+ my.numActiveRef = 0;
+
+ if (sts > 0) {
+ count = sts;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::update: Updating indom " << pmInDomStr(my.id)
+ << ": Got " << count << " instances (vs " << numInsts()
+ << ")" << endl;
+ }
+
+ // Any instances which are not in the new indom AND are not
+ // referenced can be removed
+ for (i = 0; i < oldLen; i++) {
+ QmcInstance &inst = my.instances[i];
+ inst.setActive(false);
+
+ if (inst.refCount() || inst.null())
+ continue;
+ j = 0;
+ if (i < count && inst.inst() == instList[i]) {
+ if (inst.name().compare(nameList[i]) == 0)
+ continue;
+ else
+ j = count;
+ }
+ for (; j < count; j++) {
+ if (inst.inst() == instList[j]) {
+ if (inst.name().compare(nameList[j]) == 0)
+ break;
+ else
+ j = count;
+ }
+ }
+
+ // If j >= count, then instance i has either changed or gone away
+ if (j >= count) {
+ inst.deactivate(my.nullIndex);
+ my.nullIndex = i;
+ my.nullCount++;
+ my.profile = true;
+ }
+ }
+
+ for (i = 0; i < count; i++) {
+ // Quick check to see if they are the same
+ if (i < my.instances.size() &&
+ my.instances[i].inst() == instList[i] &&
+ my.instances[i].name().compare(nameList[i]) == 0) {
+ if (pmDebug & DBG_TRACE_INDOM) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::update: Unchanged \"" << nameList[i]
+ << "\"(" << instList[i] << ')' << endl;
+ }
+ my.instances[i].setActive(true);
+ my.numActive++;
+ if (my.instances[i].refCount())
+ my.numActiveRef++;
+ continue;
+ }
+
+ for (j = 0; j < oldLen; j++) {
+ if (my.instances[j].null())
+ continue;
+
+ if (my.instances[j].inst() == instList[i]) {
+ // Same instance and same external name but different
+ // order, mark as active. If it has a different
+ // external name just ignore it
+ if (my.instances[j].name().compare(nameList[i]) == 0) {
+ my.instances[j].setActive(true);
+ my.numActive++;
+ if (my.instances[j].refCount())
+ my.numActiveRef++;
+ }
+ else if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::update: Ignoring \""
+ << nameList[i]
+ << "\" with identical internal identifier ("
+ << instList[i] << ")" << endl;
+ }
+ break;
+ }
+ }
+
+ if (j == oldLen) {
+ if (pmDebug & DBG_TRACE_INDOM) {
+ QTextStream cerr(stderr);
+ cerr << "QmcIndom::update: Adding \"" << nameList[i]
+ << "\"(" << instList[i] << ")" << endl;
+ }
+ if (my.nullCount) {
+ uint newindex = my.instances[my.nullIndex].index();
+ my.instances[my.nullIndex] = QmcInstance(instList[i],
+ nameList[i]);
+ my.nullIndex = newindex;
+ my.nullCount--;
+ }
+ else
+ my.instances.append(QmcInstance(instList[i], nameList[i]));
+ my.profile = true;
+ my.numActive++;
+ }
+ }
+
+ free(instList);
+ free(nameList);
+
+ if (pmDebug & DBG_TRACE_INDOM) {
+ QTextStream cerr(stderr);
+ if (my.instances.size() == oldLen && my.nullCount == oldNullCount)
+ cerr << "QmcIndom::update: indom size unchanged" << endl;
+ else {
+ cerr << "QmcIndom::update: indom changed from "
+ << oldLen - oldNullCount << " to " << numInsts() << endl;
+ dump(cerr);
+ }
+ }
+ }
+ else {
+ for (i = 0; i < my.instances.size(); i++)
+ my.instances[i].setActive(false);
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ if (sts == 0)
+ cerr << "QmcIndom::update: indom empty!" << endl;
+ else
+ cerr << "QmcIndom::update: unable to lookup "
+ << pmInDomStr(my.id) << " from host/local source: "
+ << pmErrStr(sts) << endl;
+ }
+ }
+
+ return sts;
+}
diff --git a/src/libpcp_qmc/src/qmc_indom.h b/src/libpcp_qmc/src/qmc_indom.h
new file mode 100644
index 0000000..265f3ad
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_indom.h
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ */
+#ifndef QMC_INDOM_H
+#define QMC_INDOM_H
+
+#include "qmc.h"
+
+#include <qlist.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+class QmcInstance
+{
+public:
+ QmcInstance();
+ QmcInstance(int id, const char* name);
+ QmcInstance const& operator=(QmcInstance const&);
+
+ void deactivate(int index);
+ int inst() const { return my.inst; }
+ bool null() const { return (my.inst == (int)PM_IN_NULL); }
+ QString name() const { return my.name; }
+ int refCount() const { return my.refCount; }
+ int refCountInc() { return ++my.refCount; }
+ int refCountDec() { return --my.refCount; }
+ bool active() const { return my.active; }
+ void setActive(bool active) { my.active = active; }
+ int index() const { return my.index; }
+ void setIndex(int index) { my.index = index; }
+
+private:
+ struct {
+ int inst; // Instance internal id
+ QString name; // Instance external id
+ int refCount;
+ int index; // Index into pmResult of last fetch
+ // May also be used to index the next NULL inst
+ bool active; // Instance was listed in last indom lookup
+ } my;
+};
+
+class QmcIndom
+{
+public:
+ QmcIndom(int type, QmcDesc &desc);
+
+ int status() const { return my.status; }
+ int id() const { return my.id; }
+
+ int numInsts() const { return my.instances.size() - my.nullCount; }
+ int numActiveInsts() const { return my.numActive; }
+
+ // Length of instance list - some of the instances may be NULL hence
+ // this may be larger than numInsts()
+ int listLen() const { return my.instances.size(); }
+
+ // Internal instance id for instance <index>
+ int inst(uint index) const { return my.instances[index].inst(); }
+
+ // External instance name for instance <index>
+ const QString name(uint index) const { return my.instances[index].name(); }
+
+ bool nullInst(uint index) const { return my.instances[index].null(); }
+
+ // Was this instance listed in the last update?
+ bool activeInst(uint index) const { return my.instances[index].active(); }
+
+ // Is this instance referenced by any metrics?
+ bool refInst(uint index) const { return my.instances[index].refCount()>0; }
+
+ // Return index into table for instance with external <name>
+ // Also adds a reference to this instance for the profile
+ int lookup(QString const& name);
+
+ // Add a reference to all instances in this indom
+ // Id <active> is set, only reference active instances
+ void refAll(bool active = false);
+
+ // Remove a reference to an instance
+ void removeRef(uint index);
+
+ // Number of instances referenced
+ uint refCount() const { return my.count; }
+
+ // Was the indom different on the last fetch?
+ bool changed() const { return my.changed; }
+
+ // About to fetch, so mark this indom as unchanged
+ void newFetch() { my.changed = false; my.updated = true; }
+
+ // Mark that the indom was different in a previous fetch
+ void hasChanged() { my.changed = true; my.updated = false; }
+
+ int update(); // Update indom with latest instances
+
+ // Has profile changed since last call to genProfile
+ bool diffProfile() const { return my.profile; }
+
+ int genProfile(); // Generate profile for current context
+
+ // Likely index into pmResult for instance <inst>
+ int index(uint inst) const { return my.instances[inst].index(); }
+ void setIndex(uint inst, int index) { my.instances[inst].setIndex(index); }
+
+ // Dump some debugging output for this indom
+ void dump(QTextStream &os) const;
+
+private:
+ struct {
+ int status;
+ int type;
+ pmInDom id;
+ QList<QmcInstance> instances; // Sparse list of instances
+ bool profile; // Does the profile need to be updated
+ bool changed; // Did indom change in the last fetch?
+ bool updated; // Has the indom been updated?
+ uint count; // Number of referenced instances
+ uint nullCount; // Count of NULL instances
+ uint nullIndex; // Index to first NULL instance
+ uint numActive; // Number of active instances
+ uint numActiveRef; // Number of active referenced insts
+ } my;
+};
+
+#endif // QMC_INDOM_H
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);
+}
diff --git a/src/libpcp_qmc/src/qmc_metric.h b/src/libpcp_qmc/src/qmc_metric.h
new file mode 100644
index 0000000..7bca55b
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_metric.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2012-2014 Red Hat, Inc.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ * Copyright (c) 1998-2005 Silicon Graphics, Inc. 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.
+ */
+#ifndef QMC_METRIC_H
+#define QMC_METRIC_H
+
+#include "qmc.h"
+#include "qmc_desc.h"
+#include "qmc_group.h"
+#include "qmc_context.h"
+
+#include <qlist.h>
+#include <qvector.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+class QmcMetricValue;
+
+class QmcEventParameter
+{
+public:
+ QmcEventParameter() { my.pmid = PM_ID_NULL; }
+
+ void setPMID(pmID pmid) { my.pmid = pmid; }
+ void setNamePtr(QString *name) { my.name = name; }
+ void setDescPtr(QmcDesc *desc) { my.desc = desc; }
+ void setIndomPtr(QmcIndom *indom) { my.indom = indom; }
+
+ void setValueCount(int numInst);
+ QmcMetricValue *valuePtr(int inst);
+
+ int type() const;
+ double value(int inst) const;
+ QString stringValue(int inst) const;
+
+ void summary(QString &os, int instID) const;
+ void dump(QTextStream &os, int instID) const;
+
+private:
+ struct {
+ pmID pmid; // pmid for the parameter to an event
+ QString *name; // direct pointer into external cache
+ QmcDesc *desc; // direct pointer into external cache
+ QmcIndom *indom; // direct pointer into external cache
+ QVector<QmcMetricValue> values;
+ } my;
+};
+
+class QmcEventRecord
+{
+public:
+ QmcEventRecord() { my.missed = my.flags = 0; }
+
+ const struct timeval *timestamp() const { return &my.timestamp; }
+ void setTimestamp(struct timeval *tv) { my.timestamp = *tv; }
+
+ int flags() const { return my.flags; }
+ void setFlags(int flags) { my.flags = flags; }
+
+ int missed() const { return my.missed; }
+ void setMissed(int missed) { my.missed = missed; }
+
+ void setParameterCount(int numParams)
+ { my.parameters.resize(numParams); }
+ int setParameter(int n, pmID pmid, QmcContext *cp, pmValueSet const *vp);
+
+ QString parent() const;
+ QString identifier() const;
+
+ void parameterSummary(QString &os, int instID) const;
+ void dump(QTextStream &os, int instID, uint recordID) const;
+
+ static pmID eventFlags();
+ static pmID eventMissed();
+
+private:
+ QString parameterAsString(int index) const;
+
+ struct {
+ struct timeval timestamp;
+ int missed;
+ int flags;
+ QVector<QmcEventParameter> parameters;
+ } my;
+};
+
+class QmcMetricValue
+{
+public:
+ QmcMetricValue();
+ QmcMetricValue const& operator=(QmcMetricValue const& rhs);
+
+ int instance() const { return my.instance; }
+ void setInstance(int instance) { my.instance = instance; }
+
+ int error() const { return my.error; }
+ void setError(int error) { my.error = error; }
+ void setAllErrors(int error)
+ { my.error = my.currentError = my.previousError = error; }
+
+ double value() const { return my.value; }
+ void setValue(double value) { my.value = value; }
+ void divValue(double value) { my.value /= value; }
+ void addValue(double value) { my.value += value; }
+ void subValue(double value) { my.value -= value; }
+
+ QString stringValue() const { return my.stringValue; }
+ void setStringValue(const char *s) { my.stringValue = s; }
+
+ int currentError() const { return my.currentError; }
+ void setCurrentError(int error)
+ { my.currentError = error; resetCurrentValue(); }
+ double currentValue() const { return my.currentValue; }
+ void setCurrentValue(double value) { my.currentValue = value; }
+
+ int previousError() const { return my.previousError; }
+ double previousValue() const { return my.previousValue; }
+ void shiftValues() { my.previousValue = my.currentValue;
+ my.previousError = my.currentError;
+ my.currentError = 0; }
+
+ QVector<QmcEventRecord> const &eventRecords() const { return my.eventRecords; }
+ void extractEventRecords(QmcContext *context, int recordCount, pmResult **result);
+ void dumpEventRecords(QTextStream &os, int instid) const;
+
+private:
+ void resetCurrentValue()
+ { my.currentValue = 0.0; my.stringValue = QString::null; my.eventRecords.clear(); }
+
+ struct {
+ int instance;
+ int error;
+ double value;
+ double previousValue;
+ double currentValue;
+ int currentError;
+ int previousError;
+ QString stringValue;
+ QVector<QmcEventRecord> eventRecords;
+ } my;
+};
+
+class QmcMetric
+{
+ friend class QmcGroup;
+ friend class QmcContext;
+
+public:
+ QmcMetric(QmcGroup *group, const char *str, double theScale = 0.0,
+ bool active = false);
+ QmcMetric(QmcGroup *group, pmMetricSpec *theMetric, double theScale = 0.0,
+ bool active = false);
+ ~QmcMetric();
+
+ int status() const { return my.status; }
+ pmID metricID() const { return my.pmid; }
+ const QString name() const { return my.name; }
+ char *nameAscii() const { return strdup((const char *)my.name.toAscii()); }
+ QmcContext *context() const
+ { return my.group->context(my.contextIndex); }
+ const QmcDesc &desc() const
+ { return context()->desc(my.pmid); }
+ bool hasIndom() const
+ { return desc().desc().indom != PM_INDOM_NULL; }
+ bool hasInstances() const
+ { return (my.status >= 0 && my.indomIndex < UINT_MAX); }
+
+ // Were the instances explicitly listed?
+ bool explicitInsts() const { return my.explicitInst; }
+
+ // Are only active instances referenced
+ bool activeInsts() const { return my.active; }
+
+ int numInst() const
+ { return (my.status >= 0 && my.indomIndex < UINT_MAX) ?
+ my.values.size() : 0; }
+
+ // How many values does it have (will not equal number of instances
+ // if singular)
+ int numValues() const { return (my.status >= 0) ? my.values.size() : 0; }
+
+ // The metric indom
+ QmcIndom *indom() const
+ { return (my.indomIndex == UINT_MAX) ? NULL :
+ &(my.group->context(my.contextIndex)->indom(my.indomIndex)); }
+
+ // Internal instance id for instance <index>
+ int instID(int index) const
+ { return my.group->context(my.contextIndex)->indom(my.indomIndex).inst(my.values[index].instance()); }
+
+ // External instance name for instance <index>
+ const QString instName(int index) const
+ { return my.group->context(my.contextIndex)->indom(my.indomIndex).name(my.values[index].instance()); }
+
+ // Return the index for the instance in the indom list
+ int instIndex(uint index) const { return my.values[index].instance(); }
+
+ // Update the metric to include new instances
+ // Returns true if the instance list changed
+ // Metrics with implicit instances will be extended to include those
+ // new instances. The position of instances may change.
+ // If <active> is set, only those instances in the latest indom will
+ // be listed, other instances will be removed
+ bool updateIndom();
+
+ int addInst(QString const& name);
+ void removeInst(uint index);
+
+ // Scaling modifier applied to metric values
+ double scale() const { return my.scale; }
+
+ // Metric has event records (as opposed to real/string/aggregate values)
+ bool event() const { return event(desc().desc().type); }
+ static bool event(int type)
+ { return type == PM_TYPE_EVENT || type == PM_TYPE_HIGHRES_EVENT; }
+
+ bool aggregate() const { return aggregate(desc().desc().type); }
+ static void aggregateAsString(pmValue const *, char *, int);
+ static bool aggregate(int type)
+ { return type == PM_TYPE_AGGREGATE || type == PM_TYPE_AGGREGATE_STATIC; }
+
+ // Metric has real values (as opposed to event/string/aggregate values)
+ bool real() const { return real(desc().desc().type); }
+ static bool real(int type)
+ { return type > PM_TYPE_NOSUPPORT && type < PM_TYPE_STRING; }
+
+ // Current rate-converted and scaled real value
+ double value(int index) const { return my.values[index].value(); }
+
+ double realValue(int index) const // Current rate-converted value
+ { return my.values[index].value() * my.scale; }
+
+ double currentValue(int index) const // Current raw value
+ { return my.values[index].currentValue(); }
+
+ QString stringValue(int index) const // Current string value
+ { return my.values[index].stringValue(); }
+
+ QVector<QmcEventRecord> const &eventRecords(int index) const
+ { return my.values[index].eventRecords(); }
+
+ int error(int index) const // Current error code (after rate-conversion)
+ { return my.values[index].error(); }
+
+ int currentError(int index) const // Current raw error code
+ { return my.values[index].currentError(); }
+
+ void shiftValues(); // Shift values in preparation for next fetch
+
+ void setError(int sts); // Set error code for all instances
+
+ void extractValues(pmValueSet const* set); // Extract values after a fetch
+
+ uint contextIndex() const // Index for context in group list
+ { return my.contextIndex; }
+
+ // Index for metric into pmResult
+ uint idIndex() const { return my.idIndex; }
+
+ // Index for indom in context list
+ uint indomIndex() const { return my.indomIndex; }
+
+ // Set the canonical units
+ void setScaleUnits(pmUnits const& units);
+
+ // Generate a metric spec
+ QString spec(bool srcFlag = false,
+ bool instFlag = false,
+ uint instance = UINT_MAX) const;
+
+ // Dump out the metric and its current value(s)
+ void dump(QTextStream &os, bool srcFlag = false,
+ uint instance = UINT_MAX) const;
+
+ // Dump out the current value
+ void dumpValue(QTextStream &os, uint instance) const;
+
+ // Dump out the metric source
+ void dumpSource(QTextStream &os) const;
+
+ // Format a value into a fixed width format
+ static const char *formatNumber(double value);
+
+ // Determine the current errors and rate-converted scaled values
+ int update();
+
+ friend QTextStream &operator<<(QTextStream &os, const QmcMetric &metric);
+
+private:
+ struct {
+ pmID pmid;
+ int status;
+ QString name;
+ QmcGroup *group;
+ QList<QmcMetricValue> values;
+ double scale;
+
+ uint contextIndex; // Index into the context list for the group
+ uint idIndex; // Index into the pmid list for the context.
+ uint indomIndex; // Index into the indom list for the context.
+
+ bool explicitInst; // Instances explicitly specified
+ bool active; // Use only active implicit insts
+ } my;
+
+ void setup(QmcGroup *group, pmMetricSpec *theMetric);
+ void setupDesc(QmcGroup *group, pmMetricSpec *theMetric);
+ void setupIndom(pmMetricSpec *theMetric);
+ void setupValues(int num);
+
+ void extractNumericMetric(pmValueSet const *vset, pmValue const *v, QmcMetricValue &vref);
+ void extractArrayMetric(pmValueSet const *vset, pmValue const *v, QmcMetricValue &vref);
+ void extractEventMetric(pmValueSet const *vset, int index, QmcMetricValue &vref);
+
+ void setIdIndex(uint index) { my.idIndex = index; }
+
+ // Dump error messages
+ void dumpAll() const;
+ void dumpErr() const;
+ void dumpErr(const char *inst) const;
+
+ // Dump out different metric flavours and their current value(s)
+ void dumpEventMetric(QTextStream &os, bool srcFlag = false,
+ uint instance = UINT_MAX) const;
+ void dumpSampledMetric(QTextStream &os, bool srcFlag = false,
+ uint instance = UINT_MAX) const;
+};
+
+#endif // QMC_METRIC_H
diff --git a/src/libpcp_qmc/src/qmc_source.cpp b/src/libpcp_qmc/src/qmc_source.cpp
new file mode 100644
index 0000000..3935217
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_source.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ * Copyright (c) 1998,2005 Silicon Graphics, Inc. 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 "qmc_source.h"
+
+QString QmcSource::localHost;
+QList<QmcSource*> QmcSource::sourceList;
+
+QmcSource::QmcSource(int type, QString &source, int flags)
+{
+ my.status = -1;
+ my.flags = flags;
+ my.type = type;
+ my.tz = 0;
+ my.dupFlag = false;
+
+ if (localHost.length() == 0) {
+ char buf[MAXHOSTNAMELEN];
+ gethostname(buf, MAXHOSTNAMELEN);
+ buf[MAXHOSTNAMELEN-1] = '\0';
+ localHost = buf;
+ }
+
+ this->retryConnect(type, source);
+}
+
+void
+QmcSource::retryConnect(int type, QString &source)
+{
+ int oldTZ;
+ int oldContext;
+ int offset;
+ int sts;
+ char *tzs;
+ QString hostSpec;
+
+ my.attrs = QString::null;
+ switch(type) {
+ case PM_CONTEXT_LOCAL:
+ my.desc = "Local context";
+ my.host = my.source = localHost;
+ my.proxy = "";
+ break;
+
+ case PM_CONTEXT_HOST:
+ my.desc = "host \"";
+ my.desc.append(source);
+ my.desc.append(QChar('\"'));
+ my.host = source;
+ my.proxy = getenv("PMPROXY_HOST");
+ if ((offset = my.host.indexOf('?')) >= 0) {
+ my.attrs = my.host;
+ my.attrs.remove(0, offset+1);
+ my.host.truncate(offset);
+ }
+ if ((offset = my.host.indexOf('@')) >= 0) {
+ my.proxy = my.host;
+ my.proxy.remove(0, offset+1);
+ }
+ my.source = my.host;
+ break;
+
+ case PM_CONTEXT_ARCHIVE:
+ my.desc = "archive \"";
+ my.desc.append(source);
+ my.desc.append(QChar('\"'));
+ my.source = source;
+ my.proxy = "";
+ break;
+ }
+
+ oldContext = pmWhichContext();
+
+ hostSpec = source;
+ if (my.attrs != QString::null)
+ hostSpec.append("?").append(my.attrs);
+
+ my.status = pmNewContext(type | my.flags, (const char *)hostSpec.toAscii());
+ if (my.status >= 0) {
+ my.handles.append(my.status);
+
+ // Fetch the server-side host name for this context, properly as of pcp 3.8.3+.
+ my.context_hostname = pmGetContextHostName (my.status); // NB: may leak memory
+ if (my.context_hostname == "") // may be returned for errors or PM_CONTEXT_LOCAL
+ my.context_hostname = localHost;
+
+ if (my.type == PM_CONTEXT_ARCHIVE) {
+ pmLogLabel lp;
+ sts = pmGetArchiveLabel(&lp);
+ if (sts < 0) {
+ pmprintf("%s: Unable to obtain log label for \"%s\": %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ my.host = "unknown?";
+ my.status = sts;
+ goto done;
+ }
+ else {
+ my.host = lp.ll_hostname;
+ my.start = lp.ll_start;
+ }
+ sts = pmGetArchiveEnd(&my.end);
+ if (sts < 0) {
+ pmprintf("%s: Unable to determine end of \"%s\": %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ my.status = sts;
+ goto done;
+ }
+ }
+ else {
+ gettimeofday(&my.start, NULL);
+ my.end = my.start;
+ }
+
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::QmcSource: Created context "
+ << my.handles.last() << " to " << my.desc << endl;
+ }
+
+ oldTZ = pmWhichZone(&tzs);
+ my.tz = pmNewContextZone();
+ if (my.tz < 0)
+ pmprintf("%s: Warning: Unable to obtain timezone for %s: %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(my.tz));
+ else {
+ sts = pmWhichZone(&tzs);
+ if (sts >= 0)
+ my.timezone = tzs;
+ else
+ pmprintf("%s: Warning: Unable to obtain timezone for %s: %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ }
+
+ if (oldTZ >= 0) {
+ sts = pmUseZone(oldTZ);
+ if (sts < 0) {
+ pmprintf("%s: Warning: Unable to switch timezones."
+ " Using timezone for %s: %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ }
+ }
+ }
+ else if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::QmcSource: Context to " << source
+ << " failed: " << pmErrStr(my.status) << endl;
+ }
+
+ done:
+ sourceList.append(this);
+
+ if (oldContext >= 0) {
+ sts = pmUseContext(oldContext);
+ if (sts < 0) {
+ pmprintf("%s: Warning: Unable to switch contexts."
+ " Using context to %s: %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ }
+ }
+}
+
+QmcSource::~QmcSource()
+{
+ int i;
+
+ for (i = 0; i < sourceList.size(); i++)
+ if (sourceList[i] == this)
+ break;
+ if (i < sourceList.size())
+ sourceList.removeAt(i);
+}
+
+QString
+QmcSource::timeString(const struct timeval *timeval)
+{
+ QString timestring;
+ char timebuf[32], *ddmm, *year;
+ struct tm tmp;
+ time_t secs = (time_t)timeval->tv_sec;
+
+ ddmm = pmCtime(&secs, timebuf);
+ ddmm[10] = '\0';
+ year = &ddmm[20];
+ year[4] = '\0';
+ pmLocaltime(&secs, &tmp);
+
+ timestring.sprintf("%02d:%02d:%02d.%03d",
+ tmp.tm_hour, tmp.tm_min, tmp.tm_sec, (int)(timeval->tv_usec/1000));
+ timestring.prepend(" ");
+ timestring.prepend(ddmm);
+ timestring.append(" ");
+ timestring.append(year);
+ return timestring;
+}
+
+QString
+QmcSource::timeStringBrief(const struct timeval *timeval)
+{
+ QString timestring;
+ struct tm tmp;
+ time_t secs = (time_t)timeval->tv_sec;
+
+ pmLocaltime(&secs, &tmp);
+ timestring.sprintf("%02d:%02d:%02d.%03d",
+ tmp.tm_hour, tmp.tm_min, tmp.tm_sec, (int)(timeval->tv_usec/1000));
+ return timestring;
+}
+
+bool
+QmcSource::compare(int type, QString &source, int flags)
+{
+ if (this->type() != type)
+ return false;
+ if (this->flags() != flags)
+ return false;
+ return this->source() == source;
+}
+
+QmcSource*
+QmcSource::getSource(int type, QString &source, int flags, bool matchHosts)
+{
+ int i;
+ QmcSource *src = NULL;
+
+ for (i = 0; i < sourceList.size(); i++) {
+ src = sourceList[i];
+ if (matchHosts && type == PM_CONTEXT_HOST) {
+ if (src->type() == PM_CONTEXT_ARCHIVE && src->host() == source) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::getSource: Matched host "
+ << source << " to archive " << src->source()
+ << " (source " << i << ")" << endl;
+ }
+ break;
+ }
+ }
+ else if (src->compare(type, source, flags)) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::getSource: Matched " << source
+ << " to source " << i << endl;
+ }
+ if (src->status() < 0)
+ src->retryConnect(type, source);
+ break;
+ }
+ }
+
+ if (i == sourceList.size() &&
+ !(matchHosts == true && type == PM_CONTEXT_HOST)) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ if (type != PM_CONTEXT_LOCAL)
+ cerr << "QmcSource::getSource: Creating new source for "
+ << source << endl;
+ else
+ cerr << "QmcSource::getSource: Creating new local context"
+ << endl;
+ }
+ src = new QmcSource(type, source, flags);
+ }
+
+ if (src == NULL && pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::getSource: Unable to map host "
+ << source << " to an arch context" << endl;
+ }
+
+ return src;
+}
+
+int
+QmcSource::dupContext()
+{
+ int sts = 0;
+
+ if (my.status < 0)
+ return my.status;
+
+ if (my.dupFlag == false && my.handles.size() == 1) {
+ sts = pmUseContext(my.handles[0]);
+ if (sts >= 0) {
+ sts = my.handles[0];
+ my.dupFlag = true;
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::dupContext: Using original context for "
+ << my.desc << endl;
+ }
+ }
+ else
+ pmprintf("%s: Error: Unable to switch to context for \"%s\": %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ }
+ else if (my.handles.size()) {
+ sts = pmUseContext(my.handles[0]);
+ if (sts >= 0) {
+ sts = pmDupContext();
+ if (sts >= 0) {
+ my.handles.append(sts);
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::dupContext: " << my.desc
+ << " duplicated, handle[" << my.handles.size() - 1
+ << "] = " << sts << endl;
+ }
+ }
+ else
+ pmprintf("%s: Error: "
+ "Unable to duplicate context to \"%s\": %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ }
+ else
+ pmprintf("%s: Error: Unable to switch to context for \"%s\": %s\n",
+ pmProgname, (const char *)my.desc.toAscii(),
+ pmErrStr(sts));
+ }
+ // No active contexts, create a new context
+ else {
+ sts = pmNewContext(my.type, sourceAscii());
+ if (sts >= 0) {
+ my.handles.append(sts);
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::dupContext: new context to " << my.desc
+ << " created, handle = " << sts << endl;
+ }
+ }
+ }
+
+ if (sts < 0 && pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::dupContext: context to " << my.desc
+ << " failed: " << pmErrStr(my.status) << endl;
+ }
+
+ return sts;
+}
+
+int
+QmcSource::delContext(int handle)
+{
+ int i;
+ int sts;
+
+ for (i = 0; i < my.handles.size(); i++)
+ if (my.handles[i] == handle)
+ break;
+
+ if (i == my.handles.size()) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::delContext: Attempt to delete " << handle
+ << " from list for " << my.desc << ", but it is not listed"
+ << endl;
+ }
+ return PM_ERR_NOCONTEXT;
+ }
+
+ sts = pmDestroyContext(my.handles[i]);
+ my.handles.removeAt(i);
+
+ // If this is a valid source, but no more contexts remain,
+ // then we should delete ourselves
+ if (my.handles.size() == 0 && my.status >= 0) {
+ if (pmDebug & DBG_TRACE_PMC) {
+ QTextStream cerr(stderr);
+ cerr << "QmcSource::delContext: No contexts remain, removing "
+ << my.desc << endl;
+ }
+ delete this;
+ }
+
+ return sts;
+}
+
+QTextStream&
+operator<<(QTextStream &stream, const QmcSource &rhs)
+{
+ stream << rhs.my.desc;
+ return stream;
+}
+
+void
+QmcSource::dump(QTextStream &stream)
+{
+ stream << " sts = " << my.status << ", type = " << my.type
+ << ", source = " << my.source << endl
+ << " host = " << my.host << ", timezone = " << my.timezone
+ << ", tz hndl = " << my.tz << endl;
+ if (my.status >= 0)
+ stream << " start = " << timeString(&my.start) << ", end = "
+ << timeString(&my.end) << ", dupFlag = "
+ << (my.dupFlag == true ? "true" : "false") << endl << " "
+ << my.handles.size() << " contexts: ";
+ for (int i = 0; i < my.handles.size(); i++)
+ stream << my.handles[i] << ' ';
+ stream << endl;
+}
+
+void
+QmcSource::dumpList(QTextStream &stream)
+{
+ stream << sourceList.size() << " sources:" << endl;
+ for (int i = 0; i < sourceList.size(); i++) {
+ stream << '[' << i << "] " << *(sourceList[i]) << endl;
+ sourceList[i]->dump(stream);
+ }
+}
diff --git a/src/libpcp_qmc/src/qmc_source.h b/src/libpcp_qmc/src/qmc_source.h
new file mode 100644
index 0000000..02b8f12
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_source.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ * Copyright (c) 2007 Aconex. All Rights Reserved.
+ * Copyright (c) 1998,2005 Silicon Graphics, Inc. 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.
+ */
+#ifndef QMC_SOURCE_H
+#define QMC_SOURCE_H
+
+#include "qmc.h"
+
+#include <qlist.h>
+#include <qstring.h>
+#include <qtextstream.h>
+
+class QmcSource
+{
+public:
+ QmcSource(int type, QString &source, int flags = 0);
+ ~QmcSource();
+
+ // Get the source description by searching the list of existing sources
+ // and returning a new source only if required.
+ // If matchHosts is true, then it will attempt to map a live context
+ // to an archive source. If no matching archive context is found,
+ // a NULL pointer is returned.
+ static QmcSource* getSource(int type, QString &source, int flags = 0,
+ bool matchHosts = false);
+
+ int status() const { return my.status; }
+ int flags() const { return my.flags; }
+ int type() const { return my.type; }
+ bool isArchive() const { return my.type == PM_CONTEXT_ARCHIVE; }
+ QString source() const { return my.source; }
+ char *sourceAscii() const { return strdup((const char*)my.source.toAscii()); }
+ QString host() const { return my.host; }
+ char *hostAscii() const { return strdup((const char *)my.host.toAscii()); }
+ QString proxy() const { return my.proxy; }
+ char *proxyAscii() const { return strdup((const char *)my.proxy.toAscii()); }
+ int tzHandle() const { return my.tz; }
+ QString timezone() const { return my.timezone; }
+ struct timeval start() const { return my.start; }
+ QString startTime() { return timeString(&my.start); }
+ struct timeval end() const { return my.end; }
+ QString endTime() { return timeString(&my.end); }
+ QString desc() const { return my.desc; }
+ char *descAscii() const { return strdup((const char *)my.desc.toAscii()); }
+ QString context_hostname() const { return my.context_hostname; }
+
+ // Number of active contexts to this source
+ uint numContexts() const { return my.handles.size(); }
+
+ // Create a new context to this source
+ int dupContext();
+
+ // Delete context to this source
+ int delContext(int handle);
+
+ // Output the source
+ friend QTextStream &operator<<(QTextStream &os, const QmcSource &rhs);
+
+ // Dump all info about a source
+ void dump(QTextStream &os);
+
+ // Dump list of known sources
+ static void dumpList(QTextStream &os);
+
+ // Local host name (from gethostname(2))
+ static QString localHost;
+
+ // Convert a time to a string (long and short forms)
+ static QString timeString(const struct timeval *timeval);
+ static QString timeStringBrief(const struct timeval *timeval);
+
+protected:
+ // retry context/connection (e.g. if it failed in the constructor)
+ void retryConnect(int type, QString &source);
+
+ // compare two sources - static so getSource() can make use of it
+ bool compare(int type, QString &source, int flags);
+
+private:
+ struct {
+ int status;
+ int type;
+ QString source;
+ QString proxy;
+ QString attrs;
+ QString host;
+ QString context_hostname; // from pmcd/archive, not from -h/-a argument
+ QString desc;
+ QString timezone;
+ QList<int> handles; // Contexts created for this source
+ struct timeval start;
+ struct timeval end;
+ int tz;
+ bool dupFlag; // Dup has been called and 1st context is in use
+ int flags;
+ } my;
+
+ static QList<QmcSource*> sourceList;
+};
+
+#endif // QMC_SOURCE_H
diff --git a/src/libpcp_qmc/src/qmc_time.cpp b/src/libpcp_qmc/src/qmc_time.cpp
new file mode 100644
index 0000000..b59db0d
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_time.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2006, Ken McDonell. All Rights Reserved.
+ * Copyright (c) 2006-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 <QtGui/QIcon>
+#include "qmc_time.h"
+
+//
+// Map icon type name to QIcon
+//
+extern QIcon *QmcTime::icon(QmcTime::Icon type)
+{
+ static QIcon icons[QmcTime::IconCount];
+ static int setup;
+
+ if (!setup) {
+ setup = 1;
+ icons[QmcTime::ForwardOn] = QIcon(":images/play_on.png");
+ icons[QmcTime::ForwardOff] = QIcon(":images/play_off.png");
+ icons[QmcTime::StoppedOn] = QIcon(":images/stop_on.png");
+ icons[QmcTime::StoppedOff] = QIcon(":images/stop_off.png");
+ icons[QmcTime::BackwardOn] = QIcon(":images/back_on.png");
+ icons[QmcTime::BackwardOff] = QIcon(":images/back_off.png");
+ icons[QmcTime::FastForwardOn] = QIcon(":images/fastfwd_on.png");
+ icons[QmcTime::FastForwardOff] = QIcon(":images/fastfwd_off.png");
+ icons[QmcTime::FastBackwardOn] = QIcon(":images/fastback_on.png");
+ icons[QmcTime::FastBackwardOff] = QIcon(":images/fastback_off.png");
+ icons[QmcTime::StepForwardOn] = QIcon(":images/stepfwd_on.png");
+ icons[QmcTime::StepForwardOff] = QIcon(":images/stepfwd_off.png");
+ icons[QmcTime::StepBackwardOn] = QIcon(":images/stepback_on.png");
+ icons[QmcTime::StepBackwardOff] = QIcon(":images/stepback_off.png");
+ }
+ return &icons[type];
+}
+
+//
+// Test for not-zeroed timeval
+//
+int QmcTime::timevalNonZero(struct timeval *a)
+{
+ return (a->tv_sec != 0 || a->tv_usec != 0);
+}
+
+//
+// a := a + b for struct timevals
+//
+void QmcTime::timevalAdd(struct timeval *a, struct timeval *b)
+{
+ a->tv_usec += b->tv_usec;
+ if (a->tv_usec > 1000000) {
+ a->tv_usec -= 1000000;
+ a->tv_sec++;
+ }
+ a->tv_sec += b->tv_sec;
+}
+
+//
+// a := a - b for struct timevals, result is never less than zero
+//
+void QmcTime::timevalSub(struct timeval *a, struct timeval *b)
+{
+ a->tv_usec -= b->tv_usec;
+ if (a->tv_usec < 0) {
+ a->tv_usec += 1000000;
+ a->tv_sec--;
+ }
+ a->tv_sec -= b->tv_sec;
+ if (a->tv_sec < 0) {
+ /* clip negative values at zero */
+ a->tv_sec = 0;
+ a->tv_usec = 0;
+ }
+}
+
+//
+// a : b for struct timevals ... <0 for a<b, ==0 for a==b, >0 for a>b
+//
+int QmcTime::timevalCompare(struct timeval *a, struct timeval *b)
+{
+ int res = (int)(a->tv_sec - b->tv_sec);
+ if (res == 0)
+ res = (int)(a->tv_usec - b->tv_usec);
+ return res;
+}
+
+//
+// Conversion from seconds (double precision) to struct timeval
+//
+void QmcTime::secondsToTimeval(double value, struct timeval *tv)
+{
+ tv->tv_sec = (time_t)value;
+ tv->tv_usec = (long)(((value - (double)tv->tv_sec) * 1000000.0));
+}
+
+//
+// Conversion from struct timeval to seconds (double precision)
+//
+double QmcTime::secondsFromTimeval(struct timeval *tv)
+{
+ return (double)tv->tv_sec + ((double)tv->tv_usec / 1000000.0);
+}
+
+//
+// Conversion from other time units into seconds
+//
+double QmcTime::unitsToSeconds(double value, QmcTime::DeltaUnits units)
+{
+ if (units == QmcTime::Milliseconds)
+ return value / 1000.0;
+ else if (units == QmcTime::Minutes)
+ return value * 60.0;
+ else if (units == QmcTime::Hours)
+ return value * (60.0 * 60.0);
+ else if (units == QmcTime::Days)
+ return value * (60.0 * 60.0 * 24.0);
+ else if (units == QmcTime::Weeks)
+ return value * (60.0 * 60.0 * 24.0 * 7.0);
+ return value;
+}
+
+//
+// Conversion from seconds into other time units
+//
+double QmcTime::secondsToUnits(double value, QmcTime::DeltaUnits units)
+{
+ switch (units) {
+ case Milliseconds:
+ value = value * 1000.0;
+ break;
+ case Minutes:
+ value = value / 60.0;
+ break;
+ case Hours:
+ value = value / (60.0 * 60.0);
+ break;
+ case Days:
+ value = value / (60.0 * 60.0 * 24.0);
+ break;
+ case Weeks:
+ value = value / (60.0 * 60.0 * 24.0 * 7.0);
+ break;
+ case Seconds:
+ default:
+ break;
+ }
+ return value;
+}
+
+double QmcTime::deltaValue(QString delta, QmcTime::DeltaUnits units)
+{
+ return QmcTime::secondsToUnits(delta.trimmed().toDouble(), units);
+}
+
+QString QmcTime::deltaString(double value, QmcTime::DeltaUnits units)
+{
+ QString delta;
+
+ value = QmcTime::secondsToUnits(value, units);
+ if ((double)(int)value == value)
+ delta.sprintf("%.2f", value);
+ else
+ delta.sprintf("%.6f", value);
+ return delta;
+}
diff --git a/src/libpcp_qmc/src/qmc_time.h b/src/libpcp_qmc/src/qmc_time.h
new file mode 100644
index 0000000..0baf59d
--- /dev/null
+++ b/src/libpcp_qmc/src/qmc_time.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2006-2009, 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.
+ */
+#ifndef QMC_TIME_H
+#define QMC_TIME_H
+
+#include <sys/time.h>
+
+class QIcon;
+
+class QmcTime
+{
+public:
+ typedef enum {
+ StoppedState = 0,
+ ForwardState = 1,
+ BackwardState = 2,
+ } State;
+
+ typedef enum {
+ StepMode = 0,
+ NormalMode = 1,
+ FastMode = 2,
+ } Mode;
+
+ typedef enum {
+ NoSource = -1,
+ HostSource = 0,
+ ArchiveSource = 1,
+ } Source;
+
+ typedef enum {
+ Set = (1<<0), // client -> server
+ Step = (1<<1), // server -> clients
+ TZ = (1<<2), // server -> clients
+ VCRMode = (1<<3), // server -> clients
+ VCRModeDrag = (1<<4), // server -> clients
+ GUIShow = (1<<5), // client -> server
+ GUIHide = (1<<6), // client -> server
+ Bounds = (1<<7), // client -> server
+ ACK = (1<<8), // client -> server (except handshake)
+ } Command;
+
+ static const unsigned int Magic = 0x54494D45; // "TIME"
+
+ typedef struct {
+ unsigned int magic;
+ unsigned int length;
+ QmcTime::Command command;
+ QmcTime::Source source;
+ QmcTime::State state;
+ QmcTime::Mode mode;
+ struct timeval delta;
+ struct timeval position;
+ struct timeval start; // archive only
+ struct timeval end; // archive only
+ unsigned char data[0]; // arbitrary length (e.g. $TZ)
+ } Packet;
+
+ typedef enum {
+ ForwardOn, ForwardOff,
+ StoppedOn, StoppedOff,
+ BackwardOn, BackwardOff,
+ FastForwardOn, FastForwardOff,
+ FastBackwardOn, FastBackwardOff,
+ StepForwardOn, StepForwardOff,
+ StepBackwardOn, StepBackwardOff,
+ IconCount
+ } Icon;
+
+ typedef enum {
+ Milliseconds,
+ Seconds,
+ Minutes,
+ Hours,
+ Days,
+ Weeks,
+ } DeltaUnits;
+
+ typedef enum {
+ DebugApp = 0x1,
+ DebugProtocol = 0x2,
+ } DebugOptions;
+
+ static const int BasePort = 43334;
+ static const int FastModeDelay = 100; // milliseconds
+ static const int DefaultDelta = 2; // seconds
+
+ static QIcon *icon(QmcTime::Icon);
+ static double defaultSpeed(double delta)
+ { return 2.0 * delta; } // num deltas per second
+ static double minimumSpeed(double delta)
+ { return 0.1 * delta; } // min deltas per second
+ static double maximumSpeed(double delta)
+ { return 1000.0 * delta; } // max deltas per second
+
+ static void timevalAdd(struct timeval *a, struct timeval *b);
+ static void timevalSub(struct timeval *a, struct timeval *b);
+ static int timevalNonZero(struct timeval *a);
+ static int timevalCompare(struct timeval *a, struct timeval *b);
+
+ static void secondsToTimeval(double value, struct timeval *tv);
+ static double secondsFromTimeval(struct timeval *tv);
+
+ static double unitsToSeconds(double value, DeltaUnits units);
+ static double secondsToUnits(double value, DeltaUnits units);
+ static QString deltaString(double value, DeltaUnits units);
+ static double deltaValue(QString delta, DeltaUnits units);
+};
+
+#endif // QMC_TIME_H