You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ds...@apache.org on 2015/07/06 23:46:44 UTC
[2/7] incubator-geode git commit: GEODE-78: Imported jvsdfx-mm from
geode-1.0.0-SNAPSHOT-2.src.tar
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/e299ea84/jvsdfx-mm/src/main/java/com/pivotal/jvsd/stats/StatFileParser.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/com/pivotal/jvsd/stats/StatFileParser.java b/jvsdfx-mm/src/main/java/com/pivotal/jvsd/stats/StatFileParser.java
new file mode 100644
index 0000000..6d95ff5
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/com/pivotal/jvsd/stats/StatFileParser.java
@@ -0,0 +1,3591 @@
+package com.pivotal.jvsd.stats;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+import java.util.zip.GZIPInputStream;
+
+
+/**
+ * Copies StatArchiveFileReader behavior and then adds to it to support usage
+ * within a VSD like tool
+ */
+
+/**
+ *
+ * @author Vince Ford
+ */
+public class StatFileParser implements StatArchiveFormat {
+
+ private final StatArchiveFile[] archives;
+ // private boolean dump;
+ private boolean closed = false;
+ /**
+ *
+ */
+ public final static String FORMAT = "yyyy/MM/dd HH:mm:ss.SSS z";
+
+ /**
+ *
+ * @param archiveNames
+ * @param filters
+ * @param autoClose
+ * @throws IOException
+ */
+ public StatFileParser(File[] archiveNames, ValueFilter[] filters, boolean autoClose)
+ throws IOException {
+ //System.out.println("StatFileParser 1");
+ this.archives = new StatArchiveFile[archiveNames.length];
+ //this.dump = Boolean.getBoolean("StatFileParser.dumpall");
+ for (int i = 0; i < archiveNames.length; i++) {
+ this.archives[i] = new StatArchiveFile(this, archiveNames[i], false, filters);
+ }
+
+ update(false, autoClose);
+
+// if (this.dump || Boolean.getBoolean("StatFileParser.dump")) {
+// this.dump(new PrintWriter(System.out));
+// }
+ }
+
+ /**
+ *
+ * @param archiveName
+ * @throws IOException
+ */
+ public StatFileParser(String archiveName) throws IOException {
+
+ this(new File[]{new File(archiveName)}, null, false);
+ //System.out.println("StatFileParser 2");
+ }
+
+// private void dump(PrintWriter printWriter) {
+// System.out.println("StatFileParser Dump 1");
+// throw new UnsupportedOperationException("Not yet implemented");
+// }
+ /**
+ *
+ */
+ public interface ValueFilter {
+
+ /**
+ * Returns true if the specified archive file matches this spec. Any
+ * archives whose name does not match this spec will not be selected for
+ * loading by this spec.
+ *
+ * @param archive
+ * @return
+ */
+ public boolean archiveMatches(File archive);
+
+ /**
+ * Returns true if the specified type name matches this spec. Any types
+ * whose name does not match this spec will not be selected for loading by
+ * this spec.
+ *
+ * @param typeName
+ * @return
+ */
+ public boolean typeMatches(String typeName);
+
+ /**
+ * Returns true if the specified statistic name matches this spec. Any stats
+ * whose name does not match this spec will not be selected for loading by
+ * this spec.
+ *
+ * @param statName
+ * @return
+ */
+ public boolean statMatches(String statName);
+
+ /**
+ * Returns true if the specified instance matches this spec. Any instance
+ * whose text id and numeric id do not match this spec will not be selected
+ * for loading by this spec.
+ *
+ * @param textId
+ * @param numericId
+ * @return
+ */
+ public boolean instanceMatches(String textId, long numericId);
+ }
+
+ /**
+ *
+ */
+ static public class StatArchiveFile {
+
+ private final StatFileParser reader;
+ private InputStream is;
+ private DataInputStream dataIn;
+ private ValueFilter[] filters;
+ private final File archiveName;
+ private /* final */ int archiveVersion;
+ private /* final */ ArchiveInfo info;
+ private final boolean compressed;
+ private boolean updateOK;
+ private final boolean dump;
+ private boolean closed = false;
+ /**
+ *
+ */
+ protected int resourceInstSize = 0;
+ /**
+ *
+ */
+ protected ResourceInst[] resourceInstTable = null;
+ private ResourceType[] resourceTypeTable = null;
+ private final TimeStampSeries timeSeries = new TimeStampSeries();
+ private final DateFormat timeFormatter
+ = new SimpleDateFormat(FORMAT);
+ private final static int BUFFER_SIZE = 1024 * 1024;
+ private final ArrayList fileComboValues = new ArrayList();
+
+ /**
+ *
+ * @param reader
+ * @param archiveName
+ * @param dump
+ * @param filters
+ * @throws IOException
+ */
+ public StatArchiveFile(StatFileParser reader,
+ File archiveName,
+ boolean dump,
+ ValueFilter[] filters)
+ throws IOException {
+ // System.out.println("StatArchiveFile 1");
+ this.reader = reader;
+ this.archiveName = archiveName;
+ this.dump = dump;
+ this.compressed = archiveName.getPath().endsWith(".gz");
+ this.is = new FileInputStream(this.archiveName);
+ if (this.compressed) {
+ this.dataIn = new DataInputStream(new BufferedInputStream(new GZIPInputStream(this.is, BUFFER_SIZE), BUFFER_SIZE));
+ } else {
+ this.dataIn = new DataInputStream(new BufferedInputStream(this.is, BUFFER_SIZE));
+ }
+ this.updateOK = this.dataIn.markSupported();
+ this.filters = createFilters(filters);
+ }
+
+ private ValueFilter[] createFilters(ValueFilter[] allFilters) {
+ // System.out.println("StatArchiveFile createFilters 1");
+ if (allFilters == null) {
+ return new ValueFilter[0];
+ }
+ ArrayList l = new ArrayList();
+ for (int i = 0; i < allFilters.length; i++) {
+ if (allFilters[i].archiveMatches(this.getFile())) {
+ l.add(allFilters[i]);
+ }
+ }
+ if (l.size() == allFilters.length) {
+ return allFilters;
+ } else {
+ ValueFilter[] result = new ValueFilter[l.size()];
+ return (ValueFilter[]) l.toArray(result);
+ }
+ }
+
+ StatFileParser getReader() {
+ // System.out.println("StatArchiveFile getReader");
+ return this.reader;
+ }
+
+ void matchSpec(StatSpec spec, List matchedValues) {
+ // System.out.println("StatArchiveFile matchSpec");
+ if (spec.getCombineType() == StatSpec.FILE) {
+ // search for previous ComboValue
+ Iterator it = this.fileComboValues.iterator();
+ while (it.hasNext()) {
+ ComboValue v = (ComboValue) it.next();
+ if (!spec.statMatches(v.getDescriptor().getName())) {
+ continue;
+ }
+ if (!spec.typeMatches(v.getType().getName())) {
+ continue;
+ }
+ ResourceInst[] resources = v.getResources();
+ for (int i = 0; i < resources.length; i++) {
+ if (!spec.instanceMatches(resources[i].getName(), resources[i].
+ getId())) {
+ continue;
+ }
+ // note: we already know the archive file matches
+ }
+ matchedValues.add(v);
+ return;
+ }
+ ArrayList l = new ArrayList();
+ matchSpec(new RawStatSpec(spec), l);
+ if (l.size() != 0) {
+ ComboValue cv = new ComboValue(l);
+ // save this in file's combo value list
+ this.fileComboValues.add(cv);
+ matchedValues.add(cv);
+ }
+ } else {
+ for (int instIdx = 0; instIdx < resourceInstSize; instIdx++) {
+ resourceInstTable[instIdx].matchSpec(spec, matchedValues);
+ }
+ }
+ }
+
+ /**
+ * Formats an archive timestamp in way consistent with GemFire log dates. It
+ * will also be formatted to reflect the time zone the archive was created
+ * in.
+ *
+ * @param ts The difference, measured in milliseconds, between the time
+ * marked by this time stamp and midnight, January 1, 1970 UTC.
+ * @return
+ */
+ public String formatTimeMillis(long ts) {
+ // System.out.println("StatArchiveFile formatTimeMillis");
+ synchronized (timeFormatter) {
+ return timeFormatter.format(new Date(ts));
+ }
+ }
+
+ /**
+ * sets the time zone this archive was written in.
+ */
+ void setTimeZone(TimeZone z) {
+ //System.out.println("StatArchiveFile setTimeZone");
+ timeFormatter.setTimeZone(z);
+ }
+
+ /**
+ * Returns the time series for this archive.
+ */
+ TimeStampSeries getTimeStamps() {
+ // System.out.println("StatArchiveFile getTimeStamps");
+ return timeSeries;
+ }
+
+ /**
+ * Checks to see if the archive has changed since the StatArchiverReader
+ * instance was created or last updated. If the archive has additional
+ * samples then those are read the resource instances maintained by the
+ * reader are updated.
+ * <p>
+ * Once closed a reader can no longer be updated.
+ *
+ * @param doReset
+ * @return true if update read some new data.
+ * @throws IOException if <code>archiveName</code> could not be opened read,
+ * or closed.
+ */
+ public boolean update(boolean doReset) throws IOException {
+ // System.out.println("StatArchiveFile update");
+ if (this.closed) {
+ return false;
+ }
+ if (!this.updateOK) {
+ throw new IOException("UPDATE_OF_THIS_TYPE_OF_FILE_IS_NOT_SUPPORTED");
+ }
+
+ if (doReset) {
+ this.dataIn.reset();
+ }
+
+ int updateTokenCount = 0;
+ while (this.readToken()) {
+ updateTokenCount++;
+ }
+ return updateTokenCount != 0;
+ }
+
+ /**
+ *
+ * @param stream
+ */
+ public void dump(PrintWriter stream) {
+ // System.out.println("StatArchiveFile dump");
+ stream.print("archive=" + archiveName);
+ if (info != null) {
+ info.dump(stream);
+ }
+ for (int i = 0; i < resourceTypeTable.length; i++) {
+ if (resourceTypeTable[i] != null) {
+ resourceTypeTable[i].dump(stream);
+ }
+ }
+ stream.print("time=");
+ timeSeries.dump(stream);
+ for (int i = 0; i < resourceInstTable.length; i++) {
+ if (resourceInstTable[i] != null) {
+ //resourceInstTable[i].dump(stream);
+ }
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public File getFile() {
+ // System.out.println("StatArchiveFile getFile");
+ return this.archiveName;
+ }
+
+ /**
+ * Closes the archive.
+ *
+ * @throws IOException
+ */
+ public void close() throws IOException {
+ // System.out.println("StatArchiveFile close");
+ if (!this.closed) {
+ this.closed = true;
+ this.is.close();
+ this.dataIn.close();
+ this.is = null;
+ this.dataIn = null;
+ int typeCount = 0;
+ if (this.resourceTypeTable != null) { // fix for bug 32320
+ for (int i = 0; i < this.resourceTypeTable.length; i++) {
+ if (this.resourceTypeTable[i] != null) {
+ if (this.resourceTypeTable[i].close()) {
+ this.resourceTypeTable[i] = null;
+ } else {
+ typeCount++;
+ }
+ }
+ }
+ ResourceType[] newTypeTable = new ResourceType[typeCount];
+ typeCount = 0;
+ for (int i = 0; i < this.resourceTypeTable.length; i++) {
+ if (this.resourceTypeTable[i] != null) {
+ newTypeTable[typeCount] = this.resourceTypeTable[i];
+ typeCount++;
+ }
+ }
+ this.resourceTypeTable = newTypeTable;
+ }
+
+ if (this.resourceInstTable != null) { // fix for bug 32320
+ int instCount = 0;
+ for (int i = 0; i < this.resourceInstTable.length; i++) {
+ if (this.resourceInstTable[i] != null) {
+ if (this.resourceInstTable[i].close()) {
+ this.resourceInstTable[i] = null;
+ } else {
+ instCount++;
+ }
+ }
+ }
+ ResourceInst[] newInstTable = new ResourceInst[instCount];
+ instCount = 0;
+ for (int i = 0; i < this.resourceInstTable.length; i++) {
+ if (this.resourceInstTable[i] != null) {
+ newInstTable[instCount] = this.resourceInstTable[i];
+ instCount++;
+ }
+ }
+ this.resourceInstTable = newInstTable;
+ this.resourceInstSize = instCount;
+ }
+ // optimize memory usage of timeSeries now that no more samples
+ this.timeSeries.shrink();
+ // filters are no longer needed since file will not be read from
+ this.filters = null;
+ }
+ }
+
+ /**
+ * Returns global information about the read archive. Returns null if no
+ * information is available.
+ *
+ * @return
+ */
+ public ArchiveInfo getArchiveInfo() {
+ // System.out.println("StatArchiveFile getArchiveInfo");
+ return this.info;
+ }
+
+ private void readHeaderToken() throws IOException {
+ // System.out.println("StatArchiveFile readHeaderToken");
+ byte archiveVersionData = dataIn.readByte();
+ long startTimeStamp = dataIn.readLong();
+ long systemId = dataIn.readLong();
+ long systemStartTimeStamp = dataIn.readLong();
+ int timeZoneOffset = dataIn.readInt();
+ String timeZoneName = dataIn.readUTF();
+ String systemDirectory = dataIn.readUTF();
+ String productVersion = dataIn.readUTF();
+ String os = dataIn.readUTF();
+ String machine = dataIn.readUTF();
+ if (archiveVersionData <= 1) {
+ throw new IOException("ARCHIVE_VERSION_0_IS_NO_LONGER_SUPPORTED");
+ }
+ if (archiveVersionData > ARCHIVE_VERSION) {
+ throw new IOException("UNSUPPORTED_ARCHIVE_VERSION_0_THE_SUPPORTED_VERSION");
+ }
+ this.archiveVersion = archiveVersionData;
+ this.info = new ArchiveInfo(this, archiveVersionData,
+ startTimeStamp, systemStartTimeStamp,
+ timeZoneOffset, timeZoneName,
+ systemDirectory, systemId,
+ productVersion, os, machine);
+ // Clear all previously read types and instances
+ this.resourceInstSize = 0;
+ this.resourceInstTable = new ResourceInst[1024];
+ this.resourceTypeTable = new ResourceType[256];
+ timeSeries.setBase(startTimeStamp);
+ if (dump) {
+ info.dump(new PrintWriter(System.out));
+ }
+ }
+
+ boolean loadType(String typeName) {
+ // System.out.println("StatArchiveFile loadType");
+ // note we don't have instance data or descriptor data yet
+ if (filters == null || filters.length == 0) {
+ return true;
+ } else {
+ for (int i = 0; i < filters.length; i++) {
+ if (filters[i].typeMatches(typeName)) {
+ return true;
+ }
+ }
+ //System.out.println("DEBUG: don't load type=" + typeName);
+ return false;
+ }
+ }
+
+ boolean loadStatDescriptor(StatDescriptor stat, ResourceType type) {
+ // note we don't have instance data yet
+ // System.out.println("StatArchiveFile loadStatDescriptor");
+ if (!type.isLoaded()) {
+ return false;
+ }
+ if (filters == null || filters.length == 0) {
+ return true;
+ } else {
+ for (int i = 0; i < filters.length; i++) {
+ if (filters[i].statMatches(stat.getName())
+ && filters[i].typeMatches(type.getName())) {
+ return true;
+ }
+ }
+ //System.out.println("DEBUG: don't load stat=" + stat.getName());
+ stat.unload();
+ return false;
+ }
+ }
+
+ boolean loadInstance(String textId, long numericId, ResourceType type) {
+ //System.out.println("StatArchiveFile loadInstance");
+ if (!type.isLoaded()) {
+ return false;
+ }
+ if (filters == null || filters.length == 0) {
+ return true;
+ } else {
+ for (int i = 0; i < filters.length; i++) {
+ if (filters[i].typeMatches(type.getName())) {
+ if (filters[i].instanceMatches(textId, numericId)) {
+ StatDescriptor[] stats = type.getStats();
+ for (int j = 0; j < stats.length; j++) {
+ if (stats[j].isLoaded()) {
+ if (filters[i].statMatches(stats[j].getName())) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ //System.out.println("DEBUG: don't load instance=" + textId);
+ //type.unload();
+ return false;
+ }
+ }
+
+ boolean loadStat(StatDescriptor stat, ResourceInst resource) {
+ // System.out.println("StatArchiveFile loadStat");
+ ResourceType type = resource.getType();
+ if (!resource.isLoaded() || !type.isLoaded() || !stat.isLoaded()) {
+ return false;
+ }
+ if (filters == null || filters.length == 0) {
+ return true;
+ } else {
+ String textId = resource.getName();
+ long numericId = resource.getId();
+ for (int i = 0; i < filters.length; i++) {
+ if (filters[i].statMatches(stat.getName())
+ && filters[i].typeMatches(type.getName())
+ && filters[i].instanceMatches(textId, numericId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private void readResourceTypeToken() throws IOException {
+ // System.out.println("StatArchiveFile readResourceTypeToken");
+ int resourceTypeId = dataIn.readInt();
+ String resourceTypeName = dataIn.readUTF();
+ String resourceTypeDesc = dataIn.readUTF();
+ int statCount = dataIn.readUnsignedShort();
+ while (resourceTypeId >= resourceTypeTable.length) {
+ ResourceType[] tmp = new ResourceType[resourceTypeTable.length + 128];
+ System.arraycopy(resourceTypeTable, 0, tmp, 0, resourceTypeTable.length);
+ resourceTypeTable = tmp;
+ }
+ assert resourceTypeTable[resourceTypeId] == null;
+
+ ResourceType rt;
+ if (loadType(resourceTypeName)) {
+ rt = new ResourceType(resourceTypeId,
+ resourceTypeName,
+ resourceTypeDesc,
+ statCount);
+ if (dump) {
+ //System.out.println("ResourceType id=" + resourceTypeId
+ // + " name=" + resourceTypeName
+ // + " statCount=" + statCount
+ // + " desc=" + resourceTypeDesc);
+ }
+ } else {
+ rt = new ResourceType(resourceTypeId, resourceTypeName, statCount);
+ if (dump) {
+ //System.out.println("Not loading ResourceType id=" + resourceTypeId
+ // + " name=" + resourceTypeName);
+ }
+ }
+ resourceTypeTable[resourceTypeId] = rt;
+ for (int i = 0; i < statCount; i++) {
+ String statName = dataIn.readUTF();
+ byte typeCode = dataIn.readByte();
+ boolean isCounter = dataIn.readBoolean();
+ boolean largerBetter = isCounter; // default
+ if (this.archiveVersion >= 4) {
+ largerBetter = dataIn.readBoolean();
+ }
+ String units = dataIn.readUTF();
+ String desc = dataIn.readUTF();
+ rt.
+ addStatDescriptor(this, i, statName, isCounter, largerBetter, typeCode, units, desc);
+ if (dump) {
+// System.out.println(" " + i + "=" + statName + " isCtr=" + isCounter
+// + " largerBetter=" + largerBetter
+// + " typeCode=" + typeCode + " units=" + units
+// + " desc=" + desc);
+ }
+ }
+ }
+
+ private void readResourceInstanceCreateToken(boolean initialize) throws IOException {
+ int resourceInstId = dataIn.readInt();
+ String name = dataIn.readUTF();
+ long id = dataIn.readLong();
+ int resourceTypeId = dataIn.readInt();
+ while (resourceInstId >= resourceInstTable.length) {
+ ResourceInst[] tmp = new ResourceInst[resourceInstTable.length + 128];
+ System.arraycopy(resourceInstTable, 0, tmp, 0, resourceInstTable.length);
+ resourceInstTable = tmp;
+ }
+ assert resourceInstTable[resourceInstId] == null;
+ if ((resourceInstId + 1) > this.resourceInstSize) {
+ this.resourceInstSize = resourceInstId + 1;
+ }
+ boolean loadInstance = loadInstance(name, id, resourceTypeTable[resourceTypeId]);
+ resourceInstTable[resourceInstId] = new ResourceInst(this, resourceInstId, name, id, resourceTypeTable[resourceTypeId], loadInstance);
+ if (dump) {
+ // System.out.println((loadInstance ? "Loaded" : "Did not load") + " resource instance " + resourceInstId);
+ // System.out.println(" name=" + name + " id=" + id + " typeId=" + resourceTypeId);
+ }
+ if (initialize) {
+ StatDescriptor[] stats = resourceInstTable[resourceInstId].getType().
+ getStats();
+ for (int i = 0; i < stats.length; i++) {
+ long v;
+ switch (stats[i].getTypeCode()) {
+ case BOOLEAN_CODE:
+ v = dataIn.readByte();
+ break;
+ case BYTE_CODE:
+ case CHAR_CODE:
+ v = dataIn.readByte();
+ break;
+ case WCHAR_CODE:
+ v = dataIn.readUnsignedShort();
+ break;
+ case SHORT_CODE:
+ v = dataIn.readShort();
+ break;
+ case INT_CODE:
+ case FLOAT_CODE:
+ case LONG_CODE:
+ case DOUBLE_CODE:
+ v = readCompactValue();
+ break;
+ default:
+ throw new IOException("UNEXPECTED_TYPECODE_VALUE_0");
+ }
+ resourceInstTable[resourceInstId].initialValue(i, v);
+ }
+ }
+ }
+
+ private void readResourceInstanceDeleteToken() throws IOException {
+ int resourceInstId = dataIn.readInt();
+ assert resourceInstTable[resourceInstId] != null;
+ resourceInstTable[resourceInstId].makeInactive();
+ if (dump) {
+ // System.out.println("Delete resource instance " + resourceInstId);
+ }
+ }
+
+ private int readResourceInstId() throws IOException {
+ /*
+ if (this.archiveVersion <= 1) {
+ return dataIn.readInt();
+ }
+ */
+ int token = dataIn.readUnsignedByte();
+ if (token <= MAX_BYTE_RESOURCE_INST_ID) {
+ return token;
+ } else if (token == ILLEGAL_RESOURCE_INST_ID_TOKEN) {
+ return ILLEGAL_RESOURCE_INST_ID;
+ } else if (token == SHORT_RESOURCE_INST_ID_TOKEN) {
+ return dataIn.readUnsignedShort();
+ } else { /* token == INT_RESOURCE_INST_ID_TOKEN */
+
+ return dataIn.readInt();
+ }
+ }
+
+ private int readTimeDelta() throws IOException {
+ int result = dataIn.readUnsignedShort();
+ if (result == INT_TIMESTAMP_TOKEN) {
+ result = dataIn.readInt();
+ }
+ return result;
+ }
+
+ private long readCompactValue() throws IOException {
+ long v = dataIn.readByte();
+ if (dump) {
+ // System.out.print("compactValue(byte1)=" + v);
+ }
+ if (v < MIN_1BYTE_COMPACT_VALUE) {
+ if (v == COMPACT_VALUE_2_TOKEN) {
+ v = dataIn.readShort();
+ if (dump) {
+ //System.out.print("compactValue(short)=" + v);
+ }
+ } else {
+ int bytesToRead = ((byte) v - COMPACT_VALUE_2_TOKEN) + 2;
+ v = dataIn.readByte(); // note the first byte will be a signed byte.
+ if (dump) {
+ System.out.print("compactValue(" + bytesToRead + ")=" + v);
+ }
+ bytesToRead--;
+ while (bytesToRead > 0) {
+ v <<= 8;
+ v |= dataIn.readUnsignedByte();
+ bytesToRead--;
+ }
+ }
+ }
+ return v;
+ }
+
+ private void readSampleToken() throws IOException {
+ int millisSinceLastSample = readTimeDelta();
+ if (dump) {
+ //System.out.println("ts=" + millisSinceLastSample);
+ }
+ int resourceInstId = readResourceInstId();
+ while (resourceInstId != ILLEGAL_RESOURCE_INST_ID) {
+ if (dump) {
+ //System.out.print(" instId=" + resourceInstId);
+ }
+ StatDescriptor[] stats = resourceInstTable[resourceInstId].getType().
+ getStats();
+ int statOffset = dataIn.readUnsignedByte();
+ while (statOffset != ILLEGAL_STAT_OFFSET) {
+ long statDeltaBits;
+ switch (stats[statOffset].getTypeCode()) {
+ case BOOLEAN_CODE:
+ statDeltaBits = dataIn.readByte();
+ break;
+ case BYTE_CODE:
+ case CHAR_CODE:
+ statDeltaBits = dataIn.readByte();
+ break;
+ case WCHAR_CODE:
+ statDeltaBits = dataIn.readUnsignedShort();
+ break;
+ case SHORT_CODE:
+ statDeltaBits = dataIn.readShort();
+ break;
+ case INT_CODE:
+ case FLOAT_CODE:
+ case LONG_CODE:
+ case DOUBLE_CODE:
+ statDeltaBits = readCompactValue();
+ break;
+ default:
+ throw new IOException("UNEXPECTED_TYPECODE_VALUE_0");
+ }
+ if (resourceInstTable[resourceInstId].
+ addValueSample(statOffset, statDeltaBits)) {
+ if (dump) {
+ //System.out.print(" [" + statOffset + "]=" + statDeltaBits);
+ }
+ }
+ statOffset = dataIn.readUnsignedByte();
+ }
+ if (dump) {
+ //System.out.println();
+ }
+ resourceInstId = readResourceInstId();
+ }
+ timeSeries.addTimeStamp(millisSinceLastSample);
+ for (int i = 0; i < resourceInstTable.length; i++) {
+ ResourceInst inst = resourceInstTable[i];
+ if (inst != null && inst.isActive()) {
+ inst.addTimeStamp();
+ }
+ }
+ }
+
+ /**
+ * Returns true if token read, false if eof.
+ */
+ private boolean readToken() throws IOException {
+ byte token;
+ try {
+ if (this.updateOK) {
+ this.dataIn.mark(BUFFER_SIZE);
+ }
+ token = this.dataIn.readByte();
+ switch (token) {
+ case HEADER_TOKEN:
+ readHeaderToken();
+ break;
+ case RESOURCE_TYPE_TOKEN:
+ readResourceTypeToken();
+ break;
+ case RESOURCE_INSTANCE_CREATE_TOKEN:
+ readResourceInstanceCreateToken(false);
+ break;
+ case RESOURCE_INSTANCE_INITIALIZE_TOKEN:
+ readResourceInstanceCreateToken(true);
+ break;
+ case RESOURCE_INSTANCE_DELETE_TOKEN:
+ readResourceInstanceDeleteToken();
+ break;
+ case SAMPLE_TOKEN:
+ readSampleToken();
+ break;
+ default:
+ throw new IOException("UNEXPECTED_TOKEN_BYTE_VALUE_0");
+ }
+ return true;
+ } catch (EOFException ignore) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the approximate amount of memory used to implement this object.
+ *
+ * @return
+ */
+// protected int getMemoryUsed() {
+// int result = 0;
+// for (int i = 0; i < resourceInstTable.length; i++) {
+// if (resourceInstTable[i] != null) {
+// result += resourceInstTable[i].getMemoryUsed();
+// }
+// }
+// return result;
+// }
+ }
+
+ /**
+ *
+ */
+ static protected abstract class AbstractValue implements StatValue {
+
+ /**
+ *
+ */
+ protected StatDescriptor descriptor;
+ /**
+ *
+ */
+ protected int filter;
+ /**
+ *
+ */
+ protected long startTime = -1;
+ /**
+ *
+ */
+ protected long endTime = -1;
+ /**
+ *
+ */
+ protected boolean statsValid = false;
+ /**
+ *
+ */
+ protected int size;
+ /**
+ *
+ */
+ protected double min;
+ /**
+ *
+ */
+ protected double max;
+ /**
+ *
+ */
+ protected double avg;
+ /**
+ *
+ */
+ protected double stddev;
+
+ /**
+ *
+ */
+ public void calcStats() {
+ if (!statsValid) {
+ getSnapshots();
+ }
+ }
+
+ public int getSnapshotsSize() {
+ calcStats();
+ return this.size;
+ }
+
+ public double getSnapshotsMinimum() {
+ calcStats();
+ return this.min;
+ }
+
+ public double getSnapshotsMaximum() {
+ calcStats();
+ return this.max;
+ }
+
+ public double getSnapshotsAverage() {
+ calcStats();
+ return this.avg;
+ }
+
+ public double getSnapshotsStandardDeviation() {
+ calcStats();
+ return this.stddev;
+ }
+
+ public StatDescriptor getDescriptor() {
+ return this.descriptor;
+ }
+
+ public int getFilter() {
+ return this.filter;
+ }
+
+ public void setFilter(int filter) {
+ if (filter != this.filter) {
+ if (filter != FILTER_NONE
+ && filter != FILTER_PERSEC
+ && filter != FILTER_PERSAMPLE) {
+ throw new IllegalArgumentException("FILTER_VALUE_0_MUST_BE_1_2_OR_3");
+ }
+ this.filter = filter;
+ this.statsValid = false;
+ }
+ }
+
+ /**
+ * Calculates each stat given the result of calling getSnapshots
+ *
+ * @param values
+ */
+ protected void calcStats(double[] values) {
+ if (statsValid) {
+ return;
+ }
+ size = values.length;
+ if (size == 0) {
+ min = 0.0;
+ max = 0.0;
+ avg = 0.0;
+ stddev = 0.0;
+ } else {
+ min = values[0];
+ max = values[0];
+ double total = values[0];
+ for (int i = 1; i < size; i++) {
+ total += values[i];
+ if (values[i] < min) {
+ min = values[i];
+ } else if (values[i] > max) {
+ max = values[i];
+ }
+ }
+ avg = total / size;
+ stddev = 0.0;
+ if (size > 1) {
+ for (int i = 0; i < size; i++) {
+ double dv = values[i] - avg;
+ stddev += (dv * dv);
+ }
+ stddev /= (size - 1);
+ stddev = Math.sqrt(stddev);
+ }
+ }
+ statsValid = true;
+ }
+
+ /**
+ * Returns a string representation of this object.
+ */
+ @Override
+ public String toString() {
+ calcStats();
+ StringBuffer result = new StringBuffer();
+ result.append(getDescriptor().getName());
+ String units = getDescriptor().getUnits();
+ if (units != null && units.length() > 0) {
+ result.append(' ').append(units);
+ }
+ if (filter == FILTER_PERSEC) {
+ result.append("/sec");
+ } else if (filter == FILTER_PERSAMPLE) {
+ result.append("/sample");
+ }
+ result.append(": samples=").append(getSnapshotsSize());
+ if (startTime != -1) {
+ result.append(" startTime=\"").append(new Date(startTime)).append("\"");
+ }
+ if (endTime != -1) {
+ result.append(" endTime=\"").append(new Date(endTime)).append("\"");
+ }
+ result.append(" min=").append(nf.format(min));
+ result.append(" max=").append(nf.format(max));
+ result.append(" average=").append(nf.format(avg));
+ result.append(" stddev=").append(nf.format(stddev));
+ return result.toString();
+ }
+ }
+
+ /**
+ *
+ */
+ static public class ArchiveInfo {
+
+ private final StatArchiveFile archive;
+ private final byte archiveVersion;
+ private final long startTimeStamp; // in milliseconds
+ private final long systemStartTimeStamp; // in milliseconds
+ private final int timeZoneOffset;
+ private final String timeZoneName;
+ private final String systemDirectory;
+ private final long systemId;
+ private final String productVersion;
+ private final String os;
+ private final String machine;
+
+ /**
+ *
+ * @param archive
+ * @param archiveVersion
+ * @param startTimeStamp
+ * @param systemStartTimeStamp
+ * @param timeZoneOffset
+ * @param timeZoneName
+ * @param systemDirectory
+ * @param systemId
+ * @param productVersion
+ * @param os
+ * @param machine
+ */
+ public ArchiveInfo(StatArchiveFile archive, byte archiveVersion,
+ long startTimeStamp, long systemStartTimeStamp,
+ int timeZoneOffset, String timeZoneName,
+ String systemDirectory, long systemId,
+ String productVersion, String os, String machine) {
+ this.archive = archive;
+ this.archiveVersion = archiveVersion;
+ this.startTimeStamp = startTimeStamp;
+ this.systemStartTimeStamp = systemStartTimeStamp;
+ this.timeZoneOffset = timeZoneOffset;
+ this.timeZoneName = timeZoneName;
+ this.systemDirectory = systemDirectory;
+ this.systemId = systemId;
+ this.productVersion = productVersion;
+ this.os = os;
+ this.machine = machine;
+ archive.setTimeZone(getTimeZone());
+ }
+
+ /**
+ * Returns the difference, measured in milliseconds, between the time the
+ * archive file was create and midnight, January 1, 1970 UTC.
+ *
+ * @return
+ */
+ public long getStartTimeMillis() {
+ return this.startTimeStamp;
+ }
+
+ /**
+ * Returns the difference, measured in milliseconds, between the time the
+ * archived system was started and midnight, January 1, 1970 UTC.
+ *
+ * @return
+ */
+ public long getSystemStartTimeMillis() {
+ return this.systemStartTimeStamp;
+ }
+
+ /**
+ * Returns a numeric id of the archived system. It can be used in
+ * conjunction with the {@link #getSystemStartTimeMillis} to uniquely
+ * identify an archived system.
+ *
+ * @return
+ */
+ public long getSystemId() {
+ return this.systemId;
+ }
+
+ /**
+ * Returns a string describing the operating system the archive was written
+ * on.
+ *
+ * @return
+ */
+ public String getOs() {
+ return this.os;
+ }
+
+ /**
+ * Returns a string describing the machine the archive was written on.
+ *
+ * @return
+ */
+ public String getMachine() {
+ return this.machine;
+ }
+
+ /**
+ * Returns the time zone used when the archive was created. This can be used
+ * to print timestamps in the same time zone that was in effect when the
+ * archive was created.
+ *
+ * @return
+ */
+ public TimeZone getTimeZone() {
+ TimeZone result = TimeZone.getTimeZone(this.timeZoneName);
+ if (result.getRawOffset() != this.timeZoneOffset) {
+ result = new SimpleTimeZone(this.timeZoneOffset, this.timeZoneName);
+ }
+ return result;
+ }
+
+ /**
+ * Returns a string containing the version of the product that wrote this
+ * archive.
+ *
+ * @return
+ */
+ public String getProductVersion() {
+ return this.productVersion;
+ }
+
+ /**
+ * Returns a numeric code that represents the format version used to encode
+ * the archive as a stream of bytes.
+ *
+ * @return
+ */
+ public int getArchiveFormatVersion() {
+ return this.archiveVersion;
+ }
+
+ /**
+ * Returns a string describing the system that this archive recorded.
+ *
+ * @return
+ */
+ public String getSystem() {
+ return this.systemDirectory;
+ }
+
+ /**
+ * Return the name of the file this archive was stored in or an empty string
+ * if the archive was not stored in a file.
+ *
+ * @return
+ */
+ public String getArchiveFileName() {
+ if (this.archive != null) {
+ return this.archive.getFile().getPath();
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * Returns a string representation of this object.
+ */
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ this.dump(new PrintWriter(sw));
+ return sw.toString();
+ }
+
+ /**
+ *
+ * @param stream
+ */
+ protected void dump(PrintWriter stream) {
+ if (archive != null) {
+ stream.println("archive=" + archive.getFile());
+ }
+ stream.println("archiveVersion=" + archiveVersion);
+ if (archive != null) {
+ stream.println("startDate=" + archive.formatTimeMillis(startTimeStamp));
+ }
+ // stream.println("startTimeStamp=" + startTimeStamp +" tz=" + timeZoneName + " tzOffset=" + timeZoneOffset);
+ stream.println("timeZone=" + getTimeZone().getDisplayName());
+ stream.println("systemDirectory=" + systemDirectory);
+ stream.println("systemStartDate=" + archive.
+ formatTimeMillis(systemStartTimeStamp));
+ stream.println("systemId=" + systemId);
+ stream.println("productVersion=" + productVersion);
+ stream.println("osInfo=" + os);
+ stream.println("machineInfo=" + machine);
+ }
+ }
+
+ static private class BitExplicitByteInterval extends BitInterval {
+
+ long firstValue;
+ long lastValue;
+ byte[] bitIntervals = null;
+
+// @Override
+// int getMemoryUsed() {
+// int result = super.getMemoryUsed() + 4 + 8 + 8 + 4;
+// if (bitIntervals != null) {
+// result += bitIntervals.length;
+// }
+// return result;
+// }
+ @Override
+ int fill(double[] values, int valueOffset, int typeCode, int skipCount) {
+ int fillcount = values.length - valueOffset; // space left in values
+ int maxCount = count - skipCount; // maximum values this interval can produce
+ if (fillcount > maxCount) {
+ fillcount = maxCount;
+ }
+ long bitValue = firstValue;
+ for (int i = 0; i < skipCount; i++) {
+ bitValue += bitIntervals[i];
+ }
+ for (int i = 0; i < fillcount; i++) {
+ bitValue += bitIntervals[skipCount + i];
+ values[valueOffset + i] = bitsToDouble(typeCode, bitValue);
+ }
+ return fillcount;
+ }
+
+// @Override
+// void dump(PrintWriter stream) {
+// stream.print("(byteIntervalCount=" + count + " start=" + firstValue);
+// for (int i = 0; i < count; i++) {
+// if (i != 0) {
+// stream.print(", ");
+// }
+// stream.print(bitIntervals[i]);
+// }
+// stream.print(")");
+// }
+ BitExplicitByteInterval(long bits, long interval, int addCount) {
+ count = addCount;
+ firstValue = bits;
+ lastValue = bits + (interval * (addCount - 1));
+ bitIntervals = new byte[count * 2];
+ bitIntervals[0] = 0;
+ for (int i = 1; i < count; i++) {
+ bitIntervals[i] = (byte) interval;
+ }
+ }
+
+ @Override
+ boolean attemptAdd(long addBits, long addInterval, int addCount) {
+ // addCount >= 2; count >= 2
+ if (addCount <= 11) {
+ if (addInterval <= Byte.MAX_VALUE && addInterval >= Byte.MIN_VALUE) {
+ long firstInterval = addBits - lastValue;
+ if (firstInterval <= Byte.MAX_VALUE && firstInterval >= Byte.MIN_VALUE) {
+ lastValue = addBits + (addInterval * (addCount - 1));
+ if ((count + addCount) >= bitIntervals.length) {
+ byte[] tmp = new byte[(count + addCount) * 2];
+ System.arraycopy(bitIntervals, 0, tmp, 0, bitIntervals.length);
+ bitIntervals = tmp;
+ }
+ bitIntervals[count++] = (byte) firstInterval;
+ for (int i = 1; i < addCount; i++) {
+ bitIntervals[count++] = (byte) addInterval;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ static private class BitExplicitShortInterval extends BitInterval {
+
+ long firstValue;
+ long lastValue;
+ short[] bitIntervals = null;
+
+// @Override
+// int getMemoryUsed() {
+// int result = super.getMemoryUsed() + 4 + 8 + 8 + 4;
+// if (bitIntervals != null) {
+// result += bitIntervals.length * 2;
+// }
+// return result;
+// }
+ @Override
+ int fill(double[] values, int valueOffset, int typeCode, int skipCount) {
+ int fillcount = values.length - valueOffset; // space left in values
+ int maxCount = count - skipCount; // maximum values this interval can produce
+ if (fillcount > maxCount) {
+ fillcount = maxCount;
+ }
+ long bitValue = firstValue;
+ for (int i = 0; i < skipCount; i++) {
+ bitValue += bitIntervals[i];
+ }
+ for (int i = 0; i < fillcount; i++) {
+ bitValue += bitIntervals[skipCount + i];
+ values[valueOffset + i] = bitsToDouble(typeCode, bitValue);
+ }
+ return fillcount;
+ }
+
+// @Override
+// void dump(PrintWriter stream) {
+// stream.print("(shortIntervalCount=" + count + " start=" + firstValue);
+// for (int i = 0; i < count; i++) {
+// if (i != 0) {
+// stream.print(", ");
+// }
+// stream.print(bitIntervals[i]);
+// }
+// stream.print(")");
+// }
+ BitExplicitShortInterval(long bits, long interval, int addCount) {
+ count = addCount;
+ firstValue = bits;
+ lastValue = bits + (interval * (addCount - 1));
+ bitIntervals = new short[count * 2];
+ bitIntervals[0] = 0;
+ for (int i = 1; i < count; i++) {
+ bitIntervals[i] = (short) interval;
+ }
+ }
+
+ @Override
+ boolean attemptAdd(long addBits, long addInterval, int addCount) {
+ // addCount >= 2; count >= 2
+ if (addCount <= 6) {
+ if (addInterval <= Short.MAX_VALUE && addInterval >= Short.MIN_VALUE) {
+ long firstInterval = addBits - lastValue;
+ if (firstInterval <= Short.MAX_VALUE && firstInterval >= Short.MIN_VALUE) {
+ lastValue = addBits + (addInterval * (addCount - 1));
+ if ((count + addCount) >= bitIntervals.length) {
+ short[] tmp = new short[(count + addCount) * 2];
+ System.arraycopy(bitIntervals, 0, tmp, 0, bitIntervals.length);
+ bitIntervals = tmp;
+ }
+ bitIntervals[count++] = (short) firstInterval;
+ for (int i = 1; i < addCount; i++) {
+ bitIntervals[count++] = (short) addInterval;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ static private class BitExplicitIntInterval extends BitInterval {
+
+ long firstValue;
+ long lastValue;
+ int[] bitIntervals = null;
+
+// @Override
+// int getMemoryUsed() {
+// int result = super.getMemoryUsed() + 4 + 8 + 8 + 4;
+// if (bitIntervals != null) {
+// result += bitIntervals.length * 4;
+// }
+// return result;
+// }
+ @Override
+ int fill(double[] values, int valueOffset, int typeCode, int skipCount) {
+ int fillcount = values.length - valueOffset; // space left in values
+ int maxCount = count - skipCount; // maximum values this interval can produce
+ if (fillcount > maxCount) {
+ fillcount = maxCount;
+ }
+ long bitValue = firstValue;
+ for (int i = 0; i < skipCount; i++) {
+ bitValue += bitIntervals[i];
+ }
+ for (int i = 0; i < fillcount; i++) {
+ bitValue += bitIntervals[skipCount + i];
+ values[valueOffset + i] = bitsToDouble(typeCode, bitValue);
+ }
+ return fillcount;
+ }
+
+// @Override
+// void dump(PrintWriter stream) {
+// stream.print("(intIntervalCount=" + count + " start=" + firstValue);
+// for (int i = 0; i < count; i++) {
+// if (i != 0) {
+// stream.print(", ");
+// }
+// stream.print(bitIntervals[i]);
+// }
+// stream.print(")");
+// }
+ BitExplicitIntInterval(long bits, long interval, int addCount) {
+ count = addCount;
+ firstValue = bits;
+ lastValue = bits + (interval * (addCount - 1));
+ bitIntervals = new int[count * 2];
+ bitIntervals[0] = 0;
+ for (int i = 1; i < count; i++) {
+ bitIntervals[i] = (int) interval;
+ }
+ }
+
+ @Override
+ boolean attemptAdd(long addBits, long addInterval, int addCount) {
+ // addCount >= 2; count >= 2
+ if (addCount <= 4) {
+ if (addInterval <= Integer.MAX_VALUE && addInterval >= Integer.MIN_VALUE) {
+ long firstInterval = addBits - lastValue;
+ if (firstInterval <= Integer.MAX_VALUE && firstInterval >= Integer.MIN_VALUE) {
+ lastValue = addBits + (addInterval * (addCount - 1));
+ if ((count + addCount) >= bitIntervals.length) {
+ int[] tmp = new int[(count + addCount) * 2];
+ System.arraycopy(bitIntervals, 0, tmp, 0, bitIntervals.length);
+ bitIntervals = tmp;
+ }
+ bitIntervals[count++] = (int) firstInterval;
+ for (int i = 1; i < addCount; i++) {
+ bitIntervals[count++] = (int) addInterval;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ static private class BitExplicitLongInterval extends BitInterval {
+
+ long[] bitArray = null;
+
+// @Override
+// int getMemoryUsed() {
+// int result = super.getMemoryUsed() + 4 + 4;
+// if (bitArray != null) {
+// result += bitArray.length * 8;
+// }
+// return result;
+// }
+ @Override
+ int fill(double[] values, int valueOffset, int typeCode, int skipCount) {
+ int fillcount = values.length - valueOffset; // space left in values
+ int maxCount = count - skipCount; // maximum values this interval can produce
+ if (fillcount > maxCount) {
+ fillcount = maxCount;
+ }
+ for (int i = 0; i < fillcount; i++) {
+ values[valueOffset + i] = bitsToDouble(typeCode, bitArray[skipCount + i]);
+ }
+ return fillcount;
+ }
+
+// @Override
+// void dump(PrintWriter stream) {
+// stream.print("(count=" + count + " ");
+// for (int i = 0; i < count; i++) {
+// if (i != 0) {
+// stream.print(", ");
+// }
+// stream.print(bitArray[i]);
+// }
+// stream.print(")");
+// }
+ BitExplicitLongInterval(long bits, long interval, int addCount) {
+ count = addCount;
+ bitArray = new long[count * 2];
+ for (int i = 0; i < count; i++) {
+ bitArray[i] = bits;
+ bits += interval;
+ }
+ }
+
+ @Override
+ boolean attemptAdd(long addBits, long addInterval, int addCount) {
+ // addCount >= 2; count >= 2
+ if (addCount <= 3) {
+ if ((count + addCount) >= bitArray.length) {
+ long[] tmp = new long[(count + addCount) * 2];
+ System.arraycopy(bitArray, 0, tmp, 0, bitArray.length);
+ bitArray = tmp;
+ }
+ for (int i = 0; i < addCount; i++) {
+ bitArray[count++] = addBits;
+ addBits += addInterval;
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ static private class BitSeries {
+
+ int count; // number of items in this series
+ long currentStartBits;
+ long currentEndBits;
+ long currentInterval;
+ int currentCount;
+ int intervalIdx; // index of most recent BitInterval
+ BitInterval intervals[];
+
+ /**
+ * Returns the amount of memory used to implement this series.
+ */
+// protected int getMemoryUsed() {
+// int result = 4 + 8 + 8 + 8 + 4 + 4 + 4;
+// if (intervals != null) {
+// result += 4 * intervals.length;
+// for (int i = 0; i <= intervalIdx; i++) {
+// result += intervals[i].getMemoryUsed();
+// }
+// }
+// return result;
+// }
+ public double[] getValues(int typeCode) {
+ return getValuesEx(typeCode, 0, getSize());
+ }
+
+ /**
+ * Gets the first "resultSize" values of this series skipping over the first
+ * "samplesToSkip" ones. The first value in a series is at index 0. The
+ * maximum result size can be obtained by calling "getSize()".
+ */
+ public double[] getValuesEx(int typeCode, int samplesToSkip, int resultSize) {
+ double[] result = new double[resultSize];
+ int firstInterval = 0;
+ int idx = 0;
+ while (samplesToSkip > 0
+ && firstInterval <= intervalIdx
+ && intervals[firstInterval].getSampleCount() <= samplesToSkip) {
+ samplesToSkip -= intervals[firstInterval].getSampleCount();
+ firstInterval++;
+ }
+ for (int i = firstInterval; i <= intervalIdx; i++) {
+ idx += intervals[i].fill(result, idx, typeCode, samplesToSkip);
+ samplesToSkip = 0;
+ }
+ if (currentCount != 0) {
+ idx += BitInterval.
+ create(currentStartBits, currentInterval, currentCount).
+ fill(result, idx, typeCode, samplesToSkip);
+ }
+ // assert
+ if (idx != resultSize) {
+ throw new RuntimeException("GETVALUESEX_DIDNT_FILL_THE_LAST_0_ENTRIES_OF_ITS_RESULT");
+ }
+ return result;
+ }
+
+// void dump(PrintWriter stream) {
+// stream.print("[size=" + count + " intervals=" + (intervalIdx + 1)
+// + " memused=" + getMemoryUsed() + " ");
+// for (int i = 0; i <= intervalIdx; i++) {
+// if (i != 0) {
+// stream.print(", ");
+// }
+// intervals[i].dump(stream);
+// }
+// if (currentCount != 0) {
+// if (intervalIdx != -1) {
+// stream.print(", ");
+// }
+// BitInterval.create(currentStartBits, currentInterval, currentCount).dump(stream);
+// }
+// stream.println("]");
+// }
+ BitSeries() {
+ count = 0;
+ currentStartBits = 0;
+ currentEndBits = 0;
+ currentInterval = 0;
+ currentCount = 0;
+ intervalIdx = -1;
+ intervals = null;
+ }
+
+ void initialBits(long bits) {
+ this.currentEndBits = bits;
+ }
+
+ int getSize() {
+ return this.count;
+ }
+
+ void addBits(long deltaBits) {
+ long bits = currentEndBits + deltaBits;
+ if (currentCount == 0) {
+ currentStartBits = bits;
+ currentCount = 1;
+ } else if (currentCount == 1) {
+ currentInterval = deltaBits;
+ currentCount++;
+ } else if (deltaBits == currentInterval) {
+ currentCount++;
+ } else {
+ // we need to move currentBits into a BitInterval
+ if (intervalIdx == -1) {
+ intervals = new BitInterval[2];
+ intervalIdx = 0;
+ intervals[0] = BitInterval.
+ create(currentStartBits, currentInterval, currentCount);
+ } else {
+ if (!intervals[intervalIdx].
+ attemptAdd(currentStartBits, currentInterval, currentCount)) {
+ // wouldn't fit in current bit interval so add a new one
+ intervalIdx++;
+ if (intervalIdx >= intervals.length) {
+ BitInterval[] tmp = new BitInterval[intervals.length * 2];
+ System.arraycopy(intervals, 0, tmp, 0, intervals.length);
+ intervals = tmp;
+ }
+ intervals[intervalIdx] = BitInterval.
+ create(currentStartBits, currentInterval, currentCount);
+ }
+ }
+ // now start a new currentBits
+ currentStartBits = bits;
+ currentCount = 1;
+ }
+ currentEndBits = bits;
+ count++;
+ }
+
+ /**
+ * Free up any unused memory
+ */
+ void shrink() {
+ if (intervals != null) {
+ int currentSize = intervalIdx + 1;
+ if (currentSize < intervals.length) {
+ BitInterval[] tmp = new BitInterval[currentSize];
+ System.arraycopy(intervals, 0, tmp, 0, currentSize);
+ intervals = tmp;
+ }
+ }
+ }
+ }
+
+ static private class TimeStampSeries {
+
+ static private final int GROW_SIZE = 256;
+ int count; // number of items in this series
+ long base; // millis since midnight, Jan 1, 1970 UTC.
+ long[] timeStamps = new long[GROW_SIZE]; // elapsed millis from base
+
+ void dump(PrintWriter stream) {
+ stream.print("[size=" + count);
+ for (int i = 0; i < count; i++) {
+ if (i != 0) {
+ stream.print(", ");
+ stream.print(timeStamps[i] - timeStamps[i - 1]);
+ } else {
+ stream.print(" " + timeStamps[i]);
+ }
+ }
+ stream.println("]");
+ }
+
+ void shrink() {
+ if (count < timeStamps.length) {
+ long[] tmp = new long[count];
+ System.arraycopy(timeStamps, 0, tmp, 0, count);
+ timeStamps = tmp;
+ }
+ }
+
+ TimeStampSeries() {
+ count = 0;
+ base = 0;
+ }
+
+ void setBase(long base) {
+ this.base = base;
+ }
+
+ int getSize() {
+ return this.count;
+ }
+
+ void addTimeStamp(int ts) {
+ if (count >= timeStamps.length) {
+ long[] tmp = new long[timeStamps.length + GROW_SIZE];
+ System.arraycopy(timeStamps, 0, tmp, 0, timeStamps.length);
+ timeStamps = tmp;
+ }
+ if (count != 0) {
+ timeStamps[count] = timeStamps[count - 1] + ts;
+ } else {
+ timeStamps[count] = ts;
+ }
+ count++;
+ }
+
+ long getBase() {
+ return this.base;
+ }
+
+ /**
+ * Provides direct access to underlying data. Do not modify contents and use
+ * getSize() to keep from reading past end of array.
+ */
+ long[] getRawTimeStamps() {
+ return this.timeStamps;
+ }
+
+ long getMilliTimeStamp(int idx) {
+ return this.base + this.timeStamps[idx];
+ }
+
+ /**
+ * Returns an array of time stamp values the first of which has the
+ * specified index. Each returned time stamp is the number of millis since
+ * midnight, Jan 1, 1970 UTC.
+ */
+ double[] getTimeValuesSinceIdx(int idx) {
+ int resultSize = this.count - idx;
+ double[] result = new double[resultSize];
+ for (int i = 0; i < resultSize; i++) {
+ result[i] = getMilliTimeStamp(idx + i);
+ }
+ return result;
+ }
+ }
+
+ /**
+ *
+ */
+ static public class ResourceType {
+
+ private boolean loaded;
+// private final int id;
+ private final String name;
+ private String desc;
+ private final StatDescriptor[] stats;
+ private Map descriptorMap;
+
+ /**
+ *
+ * @param stream
+ */
+ public void dump(PrintWriter stream) {
+ if (loaded) {
+ stream.println(name + ": " + desc);
+ for (int i = 0; i < stats.length; i++) {
+ stats[i].dump(stream);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param id
+ * @param name
+ * @param statCount
+ */
+ protected ResourceType(int id, String name, int statCount) {
+ this.loaded = false;
+// this.id = id;
+ this.name = name;
+ this.desc = null;
+ this.stats = new StatDescriptor[statCount];
+ this.descriptorMap = null;
+ }
+
+ /**
+ *
+ * @param id
+ * @param name
+ * @param desc
+ * @param statCount
+ */
+ protected ResourceType(int id, String name, String desc, int statCount) {
+ this.loaded = true;
+// this.id = id;
+ this.name = name;
+ this.desc = desc;
+ this.stats = new StatDescriptor[statCount];
+ this.descriptorMap = new HashMap();
+ }
+
+ /**
+ *
+ * @return
+ */
+ public boolean isLoaded() {
+ return this.loaded;
+ }
+
+ /**
+ * Frees up any resources no longer needed after the archive file is closed.
+ * Returns true if this guy is no longer needed.
+ *
+ * @return
+ */
+ protected boolean close() {
+ if (isLoaded()) {
+ for (int i = 0; i < stats.length; i++) {
+ if (stats[i] != null) {
+ if (!stats[i].isLoaded()) {
+ stats[i] = null;
+ }
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ void unload() {
+ this.loaded = false;
+ this.desc = null;
+ for (int i = 0; i < this.stats.length; i++) {
+ this.stats[i].unload();
+ }
+ this.descriptorMap.clear();
+ this.descriptorMap = null;
+ }
+
+ /**
+ *
+ * @param archive
+ * @param offset
+ * @param name
+ * @param isCounter
+ * @param largerBetter
+ * @param typeCode
+ * @param units
+ * @param desc
+ */
+ protected void addStatDescriptor(StatArchiveFile archive, int offset, String name, boolean isCounter,
+ boolean largerBetter,
+ byte typeCode, String units, String desc) {
+ StatDescriptor descriptor = new StatDescriptor(name, offset, isCounter, largerBetter, typeCode, units, desc);
+ this.stats[offset] = descriptor;
+ if (archive.loadStatDescriptor(descriptor, this)) {
+ descriptorMap.put(name, descriptor);
+ }
+ }
+
+// private int getId() {
+// return this.id;
+// }
+ /**
+ * Returns the name of this resource type.
+ *
+ * @return
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Returns an array of descriptors for each statistic this resource type
+ * supports.
+ *
+ * @return
+ */
+ public StatDescriptor[] getStats() {
+ return this.stats;
+ }
+
+ /**
+ * Gets a stat descriptor contained in this type given the stats name.
+ *
+ * @param name the name of the stat to find in the current type
+ * @return the descriptor that matches the name or null if the type does not
+ * have a stat of the given name
+ */
+ public StatDescriptor getStat(String name) {
+ return (StatDescriptor) descriptorMap.get(name);
+ }
+
+ /**
+ * Returns a description of this resource type.
+ *
+ * @return
+ */
+ public String getDescription() {
+ return this.desc;
+ }
+ }
+
+ /**
+ *
+ */
+ static public class ResourceInst {
+
+ private final boolean loaded;
+ private final StatArchiveFile archive;
+// private final int uniqueId;
+ private final ResourceType type;
+ private final String name;
+ private final long id; //PID of process??
+ private boolean active = true;
+ private final SimpleValue[] values;
+ private int firstTSidx = -1;
+ private int lastTSidx = -1;
+
+ /**
+ * Returns the approximate amount of memory used to implement this object.
+ *
+ * @return
+ */
+// protected int getMemoryUsed() {
+// int result = 0;
+// if (values != null) {
+// for (int i = 0; i < values.length; i++) {
+// result += this.values[i].getMemoryUsed();
+// }
+// }
+// return result;
+// }
+ /**
+ *
+ * @return
+ */
+ public StatFileParser getReader() {
+ return archive.getReader();
+ }
+
+ /**
+ * Returns a string representation of this object.
+ */
+ @Override
+ public String toString() {
+ StringBuffer result = new StringBuffer();
+ result.append(name).append(", ").append("ID" + id).append(", ").
+ append(type.getName()).append(": \"").append(archive.
+ formatTimeMillis(getFirstTimeMillis())).append('\"');
+ if (!active) {
+ result.append(" inactive");
+ }
+ result.append(" samples=" + getSampleCount());
+ return result.toString();
+ }
+
+ /**
+ * Returns the number of times this resource instance has been sampled.
+ *
+ * @return
+ */
+ public int getSampleCount() {
+ if (active) {
+ return archive.getTimeStamps().getSize() - firstTSidx;
+ } else {
+ return (lastTSidx + 1) - firstTSidx;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public StatArchiveFile getArchive() {
+ return this.archive;
+ }
+
+ /**
+ *
+ * @param stream
+ */
+// protected void dump(PrintWriter stream) {
+// stream.println(name + ":"
+// + " file=" + getArchive().getFile()
+// + " id=" + id
+// + (active ? "" : " deleted")
+// + " start=" + archive.formatTimeMillis(getFirstTimeMillis()));
+// for (int i = 0; i < values.length; i++) {
+// values[i].dump(stream);
+// }
+// }
+ /**
+ *
+ * @param archive
+ * @param uniqueId
+ * @param name
+ * @param id
+ * @param type
+ * @param loaded
+ */
+ protected ResourceInst(StatArchiveFile archive, int uniqueId, String name, long id, ResourceType type, boolean loaded) {
+ this.loaded = loaded;
+ this.archive = archive;
+// this.uniqueId = uniqueId;
+ this.name = name;
+ this.id = id;
+ assert (type != null);
+ this.type = type;
+ if (loaded) {
+ StatDescriptor[] stats = type.getStats();
+ this.values = new SimpleValue[stats.length];
+ for (int i = 0; i < stats.length; i++) {
+ if (archive.loadStat(stats[i], this)) {
+ this.values[i] = new SimpleValue(this, stats[i]);
+ } else {
+ this.values[i] = null;
+ }
+ }
+ } else {
+ this.values = null;
+ }
+ }
+
+ void matchSpec(StatSpec spec, List matchedValues) {
+ if (spec.typeMatches(this.type.getName())) {
+ if (spec.instanceMatches(this.getName(), this.getId())) {
+ for (int statIdx = 0; statIdx < values.length; statIdx++) {
+ if (values[statIdx] != null) {
+ if (spec.statMatches(values[statIdx].getDescriptor().getName())) {
+ matchedValues.add(values[statIdx]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * @param statOffset
+ * @param v
+ */
+ protected void initialValue(int statOffset, long v) {
+ if (this.values != null && this.values[statOffset] != null) {
+ this.values[statOffset].initialValue(v);
+ }
+ }
+
+ /**
+ * Returns true if sample was added.
+ *
+ * @param statOffset
+ * @param statDeltaBits
+ * @return
+ */
+ protected boolean addValueSample(int statOffset, long statDeltaBits) {
+ if (this.values != null && this.values[statOffset] != null) {
+ this.values[statOffset].prepareNextBits(statDeltaBits);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ public boolean isLoaded() {
+ return this.loaded;
+ }
+
+ /**
+ * Frees up any resources no longer needed after the archive file is closed.
+ * Returns true if this guy is no longer needed.
+ *
+ * @return
+ */
+ protected boolean close() {
+ if (isLoaded()) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ values[i].shrink();
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ *
+ * @return
+ */
+ protected int getFirstTimeStampIdx() {
+ return this.firstTSidx;
+ }
+
+ /**
+ *
+ * @return
+ */
+ protected long[] getAllRawTimeStamps() {
+ return archive.getTimeStamps().getRawTimeStamps();
+ }
+
+ /**
+ *
+ * @return
+ */
+ protected long getTimeBase() {
+ return archive.getTimeStamps().getBase();
+ }
+
+ /**
+ * Returns an array of doubles containing the timestamps at which this
+ * instances samples where taken. Each of these timestamps is the
+ * difference, measured in milliseconds, between the sample time and
+ * midnight, January 1, 1970 UTC. Although these values are double they can
+ * safely be converted to <code>long</code> with no loss of information.
+ *
+ * @return
+ */
+ public double[] getSnapshotTimesMillis() {
+ return archive.getTimeStamps().getTimeValuesSinceIdx(firstTSidx);
+ }
+
+ /**
+ * Returns an array of statistic value descriptors. Each element of the
+ * array describes the corresponding statistic this instance supports. The
+ * <code>StatValue</code> instances can be used to obtain the actual sampled
+ * values of the instances statistics.
+ *
+ * @return
+ */
+ public StatValue[] getStatValues() {
+ return this.values;
+ }
+
+ /**
+ * Gets the value of the stat in the current instance given the stat name.
+ *
+ * @param name the name of the stat to find in the current instance
+ * @return the value that matches the name or null if the instance does not
+ * have a stat of the given name
+ *
+ */
+ public StatValue getStatValue(String name) {
+ StatValue result = null;
+ StatDescriptor desc = getType().getStat(name);
+ if (desc != null) {
+ result = values[desc.getOffset()];
+ }
+ return result;
+ }
+
+ /**
+ * Returns the name of this instance.
+ *
+ * @return
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Returns the id of this instance.
+ *
+ * @return
+ */
+ public long getId() {
+ return this.id;
+ }
+
+ /**
+ * Returns the difference, measured in milliseconds, between the time of the
+ * instance's first sample and midnight, January 1, 1970 UTC.
+ *
+ * @return
+ */
+ public long getFirstTimeMillis() {
+ return archive.getTimeStamps().getMilliTimeStamp(firstTSidx);
+ }
+
+ /**
+ * Returns resource type of this instance.
+ *
+ * @return
+ */
+ public ResourceType getType() {
+ return this.type;
+ }
+
+ /**
+ *
+ */
+ protected void makeInactive() {
+ this.active = false;
+ lastTSidx = archive.getTimeStamps().getSize() - 1;
+ close(); // this frees up unused memory now that no more samples
+ }
+
+ /**
+ * Returns true if archive might still have future samples for this
+ * instance.
+ *
+ * @return
+ */
+ public boolean isActive() {
+ return this.active;
+ }
+
+ /**
+ *
+ */
+ protected void addTimeStamp() {
+ if (this.loaded) {
+ if (firstTSidx == -1) {
+ firstTSidx = archive.getTimeStamps().getSize() - 1;
+ }
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ values[i].addSample();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public interface StatSpec extends ValueFilter {
+
+ /**
+ * Causes all stats that matches this spec, in all archive files, to be
+ * combined into a single global stat value.
+ */
+ public static final int GLOBAL = 2;
+ /**
+ * Causes all stats that matches this spec, in each archive file, to be
+ * combined into a single stat value for each file.
+ */
+ public static final int FILE = 1;
+ /**
+ * No combination is done.
+ */
+ public final int NONE = 0;
+
+ /**
+ * Returns one of the following values:
+ * {@link #GLOBAL}, {@link #FILE}, {@link #NONE}.
+ *
+ * @return
+ */
+ public int getCombineType();
+ }
+
+ static private class ComboValue extends AbstractValue {
+
+ private final ResourceType type;
+ private final StatValue[] values;
+
+ /**
+ * Creates a ComboValue by adding all the specified values together.
+ */
+ ComboValue(List valueList) {
+ this((StatValue[]) valueList.toArray(new StatValue[valueList.size()]));
+ }
+
+ /**
+ * Creates a ComboValue by adding all the specified values together.
+ */
+ ComboValue(StatValue[] values) {
+ this.values = values;
+ this.filter = this.values[0].getFilter();
+ String typeName = this.values[0].getType().getName();
+ String statName = this.values[0].getDescriptor().getName();
+ int bestTypeIdx = 0;
+ for (int i = 1; i < this.values.length; i++) {
+ if (this.filter != this.values[i].getFilter()) {
+ /* I'm not sure why this would happen.
+ * If it really can happen then this code should change
+ * the filter since a client has no way to select values
+ * based on the filter.
+ */
+ throw new IllegalArgumentException("CANT_COMBINE_VALUES_WITH_DIFFERENT_FILTERS");
+ }
+ if (!typeName.equals(this.values[i].getType().getName())) {
+ throw new IllegalArgumentException("CANT_COMBINE_VALUES_WITH_DIFFERENT_TYPES");
+ }
+ if (!statName.equals(this.values[i].getDescriptor().getName())) {
+ throw new IllegalArgumentException("CANT_COMBINE_DIFFERENT_STATS");
+ }
+ if (this.values[i].getDescriptor().isCounter()) {
+ // its a counter which is not the default
+ if (!this.values[i].getDescriptor().isLargerBetter()) {
+ // this guy has non-defaults for both use him
+ bestTypeIdx = i;
+ } else if (this.values[bestTypeIdx].getDescriptor().isCounter()
+ == this.values[bestTypeIdx].getDescriptor().isLargerBetter()) {
+ // as long as we haven't already found a guy with defaults
+ // make this guy the best type
+ bestTypeIdx = i;
+ }
+ } else {
+ // its a gauge, see if it has a non-default largerBetter
+ if (this.values[i].getDescriptor().isLargerBetter()) {
+ // as long as we haven't already found a guy with defaults
+ if (this.values[bestTypeIdx].getDescriptor().isCounter()
+ == this.values[bestTypeIdx].getDescriptor().isLargerBetter()) {
+ // make this guy the best type
+ bestTypeIdx = i;
+ }
+ }
+ }
+ }
+ this.type = this.values[bestTypeIdx].getType();
+ this.descriptor = this.values[bestTypeIdx].getDescriptor();
+ }
+
+ private ComboValue(ComboValue original, long startTime, long endTime) {
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.type = original.getType();
+ this.descriptor = original.getDescriptor();
+ this.filter = original.getFilter();
+ this.values = new StatValue[original.values.length];
+ for (int i = 0; i < this.values.length; i++) {
+ this.values[i] = original.values[i].createTrimmed(startTime, endTime);
+ }
+ }
+
+ public StatValue createTrimmed(long startTime, long endTime) {
+ if (startTime == this.startTime && endTime == this.endTime) {
+ return this;
+ } else {
+ return new ComboValue(this, startTime, endTime);
+ }
+ }
+
+ public ResourceType getType() {
+ return this.type;
+ }
+
+ public ResourceInst[] getResources() {
+ HashSet set = new HashSet();
+ for (int i = 0; i < values.length; i++) {
+ set.addAll(Arrays.asList(values[i].getResources()));
+ }
+ ResourceInst[] result = new ResourceInst[set.size()];
+ return (ResourceInst[]) set.toArray(result);
+ }
+
+ public boolean hasValueChanged() {
+ return true;
+ }
+
+ public static boolean closeEnough(long v1, long v2, long delta) {
+ return (v1 == v2) || ((Math.abs(v1 - v2) / 2) <= delta);
+ }
+
+ /**
+ * Return true if v is closer to prev. Return false if v is closer to next.
+ * Return true if v is the same distance from both.
+ */
+ public static boolean closer(long v, long prev, long next) {
+ return Math.abs(v - prev) <= Math.abs(v - next);
+ }
+
+ /**
+ * Return true if the current ts must be inserted instead of being mapped to
+ * the tsAtInsertPoint
+ */
+ private static boolean mustInsert(int nextIdx, long[] valueTimeStamps, long tsAtInsertPoint) {
+ return (nextIdx < valueTimeStamps.length)
+ && (valueTimeStamps[nextIdx] <= tsAtInsertPoint);
+ }
+
+ public long[] getRawAbsoluteTimeStampsWithSecondRes() {
+ return getRawAbsoluteTimeStamps();
+ }
+
+ public long[] getRawAbsoluteTimeStamps() {
+ if (values.length == 0) {
+ return new long[0];
+ }
+// for (int i=0; i < values.length; i++) {
+// System.out.println("DEBUG: inst# " + i + " has "
+// + values[i].getRawAbsoluteTimeStamps().length
+// + " timestamps");
+// }
+ long[] valueTimeStamps = values[0].getRawAbsoluteTimeStamps();
+ int tsCount = valueTimeStamps.length + 1;
+ long[] ourTimeStamps = new long[(tsCount * 2) + 1];
+ System.
+ arraycopy(valueTimeStamps, 0, ourTimeStamps, 0, valueTimeStamps.length);
+ // Note we add a MAX sample to make the insert logic simple
+ ourTimeStamps[valueTimeStamps.length] = Long.MAX_VALUE;
+ for (int i = 1; i < values.length; i++) {
+ valueTimeStamps = values[i].getRawAbsoluteTimeStamps();
+ if (valueTimeStamps.length == 0) {
+ continue;
+ }
+ int ourIdx = 0;
+ int j = 0;
+ long tsToInsert = valueTimeStamps[0] - 1000; // default to 1 second
+ if (valueTimeStamps.length > 1) {
+ tsToInsert = valueTimeStamps[0] - (valueTimeStamps[1] - valueTimeStamps[0]);
+ }
+ // tsToInsert is now initialized to a value that we can pretend
+ // was the previous timestamp inserted.
+ while (j < valueTimeStamps.length) {
+ long timeDelta = (valueTimeStamps[j] - tsToInsert) / 2;
+ tsToInsert = valueTimeStamps[j];
+ long tsAtInsertPoint = ourTimeStamps[ourIdx];
+ while (tsToInsert > tsAtInsertPoint
+ && !closeEnough(tsToInsert, tsAtInsertPoint, timeDelta)) {
+// System.out.println("DEBUG: skipping " + ourIdx + " because it was not closeEnough");
+ ourIdx++;
+ tsAtInsertPoint = ourTimeStamps[ourIdx];
+ }
+ if (closeEnough(tsToInsert, tsAtInsertPoint, timeDelta)
+ && !mustInsert(j + 1, valueTimeStamps, tsAtInsertPoint)) {
+ // It was already in our list so just go to the next one
+ j++;
+ ourIdx++; // never put the next timestamp at this index
+ while (!closer(tsToInsert, ourTimeStamps[ourIdx - 1], ourTimeStamps[ourIdx])
+ && !mustInsert(j, valueTimeStamps, ourTimeStamps[ourIdx])) {
+// System.out.println("DEBUG: skipping mergeTs[" + (ourIdx-1) + "]="
+// + tsAtInsertPoint + " because it was closer to the next one");
+ ourIdx++; // it is closer to the next one so skip forward on more
+ }
+ } else {
+ // its not in our list so add it
+ int endRunIdx = j + 1;
+ while (endRunIdx < valueTimeStamps.length
+ && valueTimeStamps[endRunIdx] < tsAtInsertPoint
+ && !closeEnough(valueTimeStamps[endRunIdx], tsAtInsertPoint, timeDelta)) {
+ endRunIdx++;
+ }
+ int numToCopy = endRunIdx - j;
+// System.out.println("DEBUG: tsToInsert=" + tsToInsert
+// + " tsAtInsertPoint=" + tsAtInsertPoint
+// + " timeDelta=" + timeDelta
+// + " vDelta=" + (Math.abs(tsToInsert-tsAtInsertPoint)/2)
+// + " numToCopy=" + numToCopy);
+// if (j > 0) {
+// System.out.println("DEBUG: prevTsToInsert=" + valueTimeStamps[j-1]);
+// }
+// if (ourIdx > 0) {
+// System.out.println("DEBUG ourTimeStamps[" + (ourIdx-1) + "]=" + ourTimeStamps[ourIdx-1]);
+// }
+
+// if (numToCopy > 1) {
+// System.out.println("DEBUG: endRunTs=" + valueTimeStamps[endRunIdx-1]);
+// }
+ if (tsCount + numToCopy > ourTimeStamps.length) {
+ // grow our timestamp array
+ long[] tmp = new long[(tsCount + numToCopy) * 2];
+ System.arraycopy(ourTimeStamps, 0, tmp, 0, tsCount);
+ ourTimeStamps = tmp;
+ }
+ // make room for insert
+ System.arraycopy(ourTimeStamps, ourIdx,
+ ourTimeStamps, ourIdx + numToCopy,
+ tsCount - ourIdx);
+ // insert the elements
+ if (numToCopy == 1) {
+ ourTimeStamps[ourIdx] = valueTimeStamps[j];
+ } else {
+ System.arraycopy(valueTimeStamps, j,
+ ourTimeStamps, ourIdx,
+ numToCopy);
+ }
+ ourIdx += numToCopy;
+ tsCount += numToCopy;
+ // skip over all inserted elements
+ j += numToCopy;
+ }
+
+ }
+ }
+
+ tsCount--;
+ {
+ int startIdx = 0;
+ int endIdx = tsCount - 1;
+ if (startTime != -1) {
+
+ assert (ourTimeStamps[startIdx] >= startTime);
+ }
+ if (endTime != -1) {
+
+ assert (endIdx == startIdx - 1 || ourTimeStamps[endIdx] < endTime);
+ }
+ tsCount = (endIdx - startIdx) + 1;
+
+ // shrink and trim our timestamp array
+ long[] tmp = new long[tsCount];
+ System.arraycopy(ourTimeStamps, startIdx, tmp, 0, tsCount);
+ ourTimeStamps = tmp;
+ }
+ return ourTimeStamps;
+ }
+
+ public double[] getRawSnapshots() {
+ return getRawSnapshots(getRawAbsoluteTimeStamps());
+ }
+
+ /**
+ * Returns true if the timeStamp at curIdx is the one that ts is the closest
+ * to. We know that timeStamps[curIdx-1], if it exists, was not the closest.
+ */
+ private static boolean isClosest(long ts, long[] timeStamps, int curIdx) {
+ if (curIdx >= (timeStamps.length - 1)) {
+ // curIdx is the last one so it must be the closest
+ return true;
+ }
+ if (ts == timeStamps[curIdx]) {
+ return true;
+ }
+ return closer(ts, timeStamps[curIdx], timeStamps[curIdx + 1]);
+ }
+
+ public boolean isTrimmedLeft() {
+ for (int i = 0; i < this.values.length; i++) {
+ if (this.values[i].isTrimmedLeft()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private double[] getRawSnapshots(long[] ourTimeStamps) {
+ double[] result = new double[ourTimeStamps.length];
+// System.out.println("DEBUG: producing " + result.length + " values");
+ if (result.length > 0) {
+ for (int i = 0; i < values.length; i++) {
+ long[] valueTimeStamps = values[i].getRawAbsoluteTimeStamps();
+ double[] valueSnapshots = values[i].getRawSnapshots();
+ double currentValue = 0.0;
+ int curIdx = 0;
+ if (values[i].isTrimmedLeft()) {
+ currentValue = valueSnapshots[0];
+ }
+ for (int j = 0; j < valueSnapshots.length; j++) {
+ while (!isClosest(valueTimeStamps[j], ourTimeStamps, curIdx)) {
+ if (descriptor.isCounter()) {
+ result[curIdx] += currentValue;
+ }
+ curIdx++;
+ }
+ if (curIdx >= result.length) {
+ // Add this to workaround bug 30288
+ int samplesSkipped = valueSnapshots.length - j;
+ StringBuffer msg = new StringBuffer(100);
+ msg.append("WARNING: dropping last ");
+ if (samplesSkipped == 1) {
+ msg.append("sample because it");
+ } else {
+ msg.append(samplesSkipped).append(" samples because they");
+ }
+ msg.append(" could not fit in the merged result.");
+ System.out.println(msg.toString());
+ break;
+ }
+ currentValue = valueSnapshots[j];
+ result[curIdx] += currentValue;
+ curIdx++;
+ }
+ if (descriptor.isCounter()) {
+ for (int j = curIdx; j < result.length; j++) {
+ result[j] += currentValue;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public double[] getSnapshots() {
+ double[] result;
+ if (filter != FILTER_NONE) {
+ long[] timestamps = getRawAbsoluteTimeStamps();
+ double[] snapshots = getRawSnapshots(timestamps);
+ if (snapshots.length <= 1) {
+ return new double[0];
+ }
+ result = new double[snapshots.length - 1];
+ for (int i = 0; i < result.length; i++) {
+ double valueDelta = snapshots[i + 1] - snapshots[i];
+ if (filter == FILTER_PERSEC) {
+ long timeDelta = timestamps[i + 1] - timestamps[i];
+ result[i] = valueDelta / (timeDelta / 1000.0);
+
+ } else {
+ result[i] = valueDelta;
+ }
+ }
+ } else {
+ result = getRawSnapshots();
+ }
+ calcStats(result);
+ return result;
+ }
+ }
+
+ private static class RawStatSpec implements StatSpec {
+
+ private final StatSpec spec;
+
+ RawStatSpec(StatSpec wrappedSpec) {
+ this.spec = wrappedSpec;
+ }
+
+ public int getCombineType() {
+ return StatSpec.NONE;
+ }
+
+ public boolean typeMatches(String typeName) {
+ return spec.typeMatches(typeName);
+ }
+
+ public boolean statMatches(String statName) {
+ return spec.statMatches(statName);
+ }
+
+ public boolean instanceMatches(String textId, long numericId) {
+ return spec.instanceMatches(textId, numericId);
+ }
+
+ public boolean archiveMatches(File archive) {
+ return spec.archiveMatches(archive);
+ }
+ }
+
+ /**
+ *
+ */
+ static public class StatDescriptor {
+
+ private boolean loaded;
+ private String name;
+ private final int offset;
+ private final boolean isCounter;
+ private final boolean largerBetter;
+ private final byte typeCode;
+ private String units;
+ private String desc;
+
+ /**
+ *
+ * @param stream
+ */
+ protected void dump(PrintWriter stream) {
+ stream.println(" " + name + ": type=" + typeCode + " offset=" + offset
+ + (isCounter ? " counter" : "")
+ + " units=" + units
+ + " largerBetter=" + largerBetter
+ + " desc=" + desc);
+ }
+
+ /**
+ *
+ * @param name
+ * @param offset
+ * @param isCounter
+ * @param largerBetter
+ * @param typeCode
+ * @param units
+ * @param desc
+ */
+ protected StatDescriptor(String name, int offset, boolean isCounter,
+ boolean largerBetter,
+ byte typeCode, String units, String desc) {
+ this.loaded = true;
+ this.name = name;
+ this.offset = offset;
+ this.isCounter = isCounter;
+ this.largerBetter = largerBetter;
+ this.typeCode = typeCode;
+ this.units = units;
+ this.desc = desc;
+ }
+
+ /**
+ *
+ * @return
+ */
+ public boolean isLoaded() {
+ return this.loaded;
+ }
+
+ void unload() {
+ this.loaded = false;
+ this.name = null;
+ this.units = null;
+ this.desc = null;
+ }
+
+ /**
+ * Returns the type code of this statistic. It will be one of the following
+ * values:
+ * <ul>
+ * <li> {@link #BOOLEAN_CODE}
+ * <li> {@link #WCHAR_CODE}
+ * <li> {@link #CHAR_CODE}
+ * <li> {@link #BYTE_CODE}
+ * <li> {@link #SHORT_CODE}
+ * <li> {@link #INT_CODE}
+ * <li> {@link #LONG_CODE}
+ * <li> {@link #FLOAT_CODE}
+ * <li> {@link #DOUBLE_CODE}
+ * </ul>
+ *
+ * @return
+ */
+ public byte getTypeCode() {
+ return this.typeCode;
+ }
+
+ /**
+ * Returns the name of this statistic.
+ *
+ * @return
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Returns true if this statistic's value will always increase.
+ *
+ * @return
+ */
+ public boolean isCounter() {
+ return this.isCounter;
+ }
+
+ /**
+ * Returns true if larger values indicate better performance.
+ *
+ * @return
+ */
+ public boolean isLargerBetter() {
+ return this.largerBetter;
+ }
+
+ /**
+ * Returns a string that describes the units this statistic measures.
+ *
+ * @return
+ */
+ public String getUnits() {
+ return this.units;
+ }
+
+ /**
+ * Returns a textual description of this statistic.
+ *
+ * @return
+ */
+ public String getDescription() {
+ return this.desc;
+ }
+
+ /**
+ * Returns the offset of this stat in its type.
+ *
+ * @return
+ */
+ public int getOffset() {
+ return this.offset;
+ }
+ }
+
+ /**
+ *
+ */
+ static public interface StatValue {
+
+ /**
+ * {@link StatArchiveReader.StatValue} filter that causes the statistic
+ * values to be unfiltered. This causes the raw values written to the
+ * archive to be used.
+ * <p>
+ * This is the default filter for non-counter statistics. To determine if a
+ * statistic is not a counter use
+ * {@link StatArchiveReader.StatDescriptor#isCounter}.
+ */
+ public static final int FILTER_NONE = 0;
+ /**
+ * {@link StatArchiveReader.StatValue} filter that causes the statistic
+ * values to be filtered to reflect how often they change per second. Since
+ * the difference between two samples is used to calculate the value this
+ * causes the {@link StatArchiveReader.StatValue} to have one less sample
+ * than {@link #FILTER_NONE}. The instance time stamp that does not have a
+ * per second value is the instance's first time stamp {@link
+ * StatArchiveReader.ResourceInst#getFirstTimeMillis}.
+ * <p>
+ * This is the default filter for counter statistics. To determine if a
+ * statistic is a counter use
+ * {@link StatArchiveReader.StatDescriptor#isCounter}.
+ */
+ public static final int FILTER_PERSEC = 1;
+ /**
+ * {@link StatArchiveReader.StatValue} filter that causes the statistic
+ * values to be filtered to reflect how much they changed between sample
+ * periods. Since the difference between two samples is used to calculate
+ * the value this causes the {@link StatArchiveReader.StatValue} to have one
+ * less sample than {@link
+ * #FILTER_NONE}. The instance time stamp that does not have a per second
+ * value is the instance's first time stamp {@link
+ * StatArchiveReader.ResourceInst#getFirstTimeMillis}.
+ */
+ public static final int FILTER_PERSAMPLE = 2;
+
+ /**
+ * Creates and returns a trimmed version of this stat value. Any samples
+ * taken before <code>startTime</code> and after <code>endTime</code> are
+ * discarded from the resulting value. Set a time parameter to
+ * <code>-1</code> to not trim that side.
+ *
+ * @param startTime
+ * @param endTime
+ * @return
+ */
+ public StatValue createTrimmed(long startTime, long endTime);
+
+ /**
+ * Returns true if value has data that has been trimmed off it by a start
+ * timestamp.
+ *
+ * @return
+ */
+ public boolean isTrimmedLeft();
+
+ /**
+ * Gets the {@link StatArchiveReader.ResourceType type} of the resources
+ * that this value belongs to.
+ *
+ * @return
+ */
+ public ResourceType getType();
+
+ /**
+ * Gets the {@link StatArchiveReader.ResourceInst resources} that this value
+ * belongs to.
+ *
+ * @return
+ */
+ public ResourceInst[] getResources();
+
+ /**
+ * Returns an array of timestamps for each unfiltered snapshot in this
+ * value. Each returned time stamp is the number of millis since midnight,
+ * Jan 1, 1970 UTC.
+ *
+ * @return
+ */
+ public long[] getRawAbsoluteTimeStamps();
+
+ /**
+ * Returns an array of timestamps for each unfiltered snapshot in this
+ * value. Each returned time stamp is the number of millis since midnight,
+ * Jan 1, 1970 UTC. The resolution is seconds.
+ *
+ * @return
+ */
+ public long[] getRawAbsoluteTimeStampsWithSecondRes();
+
+ /**
+ * Returns an array of doubles containing the unfiltered value of this
+ * statistic for each point in time that it was sampled.
+ *
+ * @return
+ */
+ public double[] getRawSnapshots();
+
+ /**
+ * Returns an array of doubles containing the filtered value of this
+ * statistic for each point in time that it was sampled.
+ *
+ * @return
+ */
+ public double[] getSnapshots();
+
+ /**
+ * Returns the number of samples taken of this statistic's value.
+ *
+ * @return
+ */
+ public int getSnapshotsSize();
+
+ /**
+ * Returns the smallest of all the samples taken of this statistic's value.
+ *
+ * @return
+ */
+ public double getSnapshotsMinimum();
+
+ /**
+ * Returns the largest of all the samples taken of this statistic's value.
+ *
+ * @return
+ */
+ public double getSnapshotsMaximum();
+
+ /**
+ * Returns the average of all the samples taken of this statistic's value.
+ *
+ * @return
+ */
+ public double getSnapshotsAverage();
+
+ /**
+ * Returns the standard deviation of all the samples taken of this
+ * statistic's value.
+ *
+ * @return
+ */
+ public double getSnapshotsStandardDeviation();
+
+ /**
+ * Returns true if sample whose value was different from previous values has
+ * been added to this StatValue since the last time this method was called.
+ *
+ * @return
+ */
+ public boolean hasValueChanged();
+
+ /**
+ * Returns the current filter used to calculate this statistic's values. It
+ * will be one of these values:
+ * <ul>
+ * <li> {@link #FILTER_NONE}
+ * <li> {@link #FILTER_PERSAMPLE}
+ * <li> {@link #FILTER_PERSEC}
+ * </ul>
+ *
+ * @return
+ */
+ public int getFilter();
+
+ /**
+ * Sets the current filter used to calculate this statistic's values. The
+ * default filter is {@link #FILTER_NONE} unless the statistic is a counter,
+ * {@link StatArchiveReader.StatDescriptor#isCounter}, in which case its
+ * {@link #FILTER_PERSEC}.
+ *
+ * @param filter It must be one of these values:
+ * <ul>
+ * <li> {@link #FILTER_NONE}
+ * <li> {@link #FILTER_PERSAMPLE}
+ * <li> {@link #FILTER_PERSEC}
+ * </ul>
+ * @throws IllegalArgumentException if <code>filter</code> is not a valid
+ * filter constant.
+ */
+ public void setFilter(int filter);
+
+ /**
+ * Returns a description of this statistic.
+ *
+ * @return
+ */
+ public StatDescriptor getDescriptor();
+ }
+
+ /**
+ *
+ */
+ static protected abstract class BitInterval {
+
+ /**
+ * Returns number of items added to values
+ */
+ abstract int fill(double[] values, int valueOffset, int typeCode, int skipCount);
+
+ // abstract void dump(PrintWriter stream);
+ abstract boolean attemptAdd(long addBits, long addInterval, int addCount);
+
+// int getMemoryUsed() {
+// return 0;
+// }
+ /**
+ *
+ */
+ protected int count;
+
+ /**
+ *
+ * @return
+ */
+ public final int getSampleCount() {
+ return this.count;
+ }
+
+ static BitInterval create(long bits, long interval, int count) {
+ if (interval == 0) {
+ if (bits <= Integer.MAX_VALUE && bits >= Integer.MIN_VALUE) {
+ return new BitZeroIntInterval((int) bits, count);
+ } else {
+ return new BitZeroLongInterval(bits, count);
+ }
+ } else if (count <= 3) {
+ if (interval <= Byte.MAX_VALUE && interval >= Byte.MIN_VALUE) {
+ return new BitExplicitByteInterval(bits, interval, count);
+ } else if (interval <= Short.MAX_VALUE && interval >= Short.MIN_VALUE) {
+ return new BitExplicitShortInterval(bits, interval, count);
+ } else if (interval <= Integer.MAX_VALUE && interval >= Integer.MIN_VALUE) {
+ return new BitExplicitIntInterval(bits, interval, count);
+ } else {
+ return new BitExplicitLongInterval(bits, interval, count);
+ }
+ } else {
+ boolean smallBits = false;
+ boolean smallInterval = false;
+ if (bits <= Integer.MAX_VALUE && bits >= Integer.MIN_VALUE) {
+ smallBits = true;
+ }
+ if (interval <= Integer.MAX_VALUE && interval >= Integer.MIN_VALUE) {
+ smallInterval = true;
+ }
+ if (smallBits) {
+ if (smallInterval) {
+ return new BitNonZeroIntIntInterval((int) bits, (int) interval, count);
+ } else {
+ return new BitNonZeroIntLongInterval((int) bits, interval, count);
+ }
+ } else {
+ if (smallInterval) {
+ return new BitNonZeroLongIntInterval(bits, (int) interval, count);
+ } else {
+ return new BitNonZeroLongLongInterval(bits, interval, count);
+ }
+ }
+ }
+ }
+ }
+
+ static private class BitNonZeroIntIntInterval extends BitNonZeroInterval {
+
+ int bits;
+ int interval;
+
+// @Override
+// int getMemoryUsed() {
+// return super.getMemoryUsed() + 8;
+// }
+ @Override
+ long getBits() {
+ return this.bits;
+ }
+
+ @Override
+ long getInterval() {
+ return this.interval;
+ }
+
+ BitNonZeroIntIntInterval(int bits, int interval, int count) {
+ super(count);
+ this.bits = bits;
+ this.interval = interval;
+ }
+ }
+
+ static private class BitNonZeroIntLongInterval extends BitNonZeroInterval {
+
+ int bits;
+ long interval;
+
+// @Override
+// int getMemoryUsed() {
+// return super.getMemoryUsed() + 12;
+// }
+ @Override
+ long getBits() {
+ return this.bits;
+ }
+
+ @Override
+ long getInterval() {
+ return this.interval;
+ }
+
+ BitNonZeroIntLongInterval(int bits, long interval, int count) {
+ super(count);
+ this.bits = bits;
+ this.interval = interval;
+ }
+ }
+
+ static private class BitNonZeroLongIntInterval extends BitNonZeroInterval {
+
+ long bits;
+ int interval;
+
+// @Override
+// int getMemoryUsed() {
+// return super.getMemoryUsed() + 12;
+// }
+ @Override
+ long getBits() {
+ return this.bits;
+ }
+
+ @Override
+ long getInterval() {
+ return this.interval;
+ }
+
+ BitNonZeroLongIntInterval(long bits, int interval, int count) {
+ super(count);
+ this.bits = bits;
+ this.interval = interval;
+ }
+ }
+
+ static private class BitNonZeroLongLongInterval extends BitNonZeroInterval {
+
+ long bits;
+ long interval;
+
+// @Override
+// int getMemoryUsed() {
+// return super.getMemoryUsed() + 16;
+// }
+ @Override
+ long getBits() {
+ return this.bits;
+ }
+
+ @Override
+ long getInterval() {
+ return this.interval;
+ }
+
+ BitNonZeroLongLongInterval(long bits, long interval, int count) {
+ super(count);
+ this.bits = bits;
+ this.interval = interval;
+ }
+ }
+
+ static private abstract class BitZeroInterval extends BitInterval {
+
+// @Override
+// int getMemoryUsed() {
+// return super.getMemoryUsed() + 4;
+// }
+ abstract long getBits();
+
+ @Override
+ int fill(double[] values, int valueOffset, int typeCode, int skipCount) {
+ int fillcount = values.length - valueOffset; // space left in values
+ int maxCount = count - skipCount; // maximum values this inter
<TRUNCATED>