You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ud...@apache.org on 2016/08/18 21:18:34 UTC
[34/50] [abbrv] incubator-geode git commit: GEODE-1781: refactor
internal statistics classes
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/750996a0/geode-core/src/main/java/com/gemstone/gemfire/internal/statistics/StatArchiveReader.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/com/gemstone/gemfire/internal/statistics/StatArchiveReader.java b/geode-core/src/main/java/com/gemstone/gemfire/internal/statistics/StatArchiveReader.java
new file mode 100644
index 0000000..12637bc
--- /dev/null
+++ b/geode-core/src/main/java/com/gemstone/gemfire/internal/statistics/StatArchiveReader.java
@@ -0,0 +1,3339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gemstone.gemfire.internal.statistics;
+
+import com.gemstone.gemfire.GemFireIOException;
+import com.gemstone.gemfire.InternalGemFireException;
+import com.gemstone.gemfire.internal.Assert;
+import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
+import com.gemstone.gemfire.internal.logging.DateFormatter;
+
+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.Set;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * StatArchiveReader provides APIs to read statistic snapshots from an archive
+ * file.
+ */
+public class StatArchiveReader implements StatArchiveFormat {
+
+ protected static final NumberFormat nf = NumberFormat.getNumberInstance();
+ static {
+ nf.setMaximumFractionDigits(2);
+ nf.setGroupingUsed(false);
+ }
+
+ private final StatArchiveFile[] archives;
+ private boolean dump;
+ private boolean closed = false;
+
+ /**
+ * Creates a StatArchiveReader that will read the named archive file.
+ * @param autoClose if its <code>true</code> then the reader will close
+ * input files as soon as it finds their end.
+ * @throws IOException if <code>archiveName</code> could not be opened
+ * read, or closed.
+ */
+ public StatArchiveReader(File[] archiveNames, ValueFilter[] filters, boolean autoClose)
+ throws IOException
+ {
+ this.archives = new StatArchiveFile[archiveNames.length];
+ this.dump = Boolean.getBoolean("StatArchiveReader.dumpall");
+ for (int i=0; i < archiveNames.length; i++) {
+ this.archives[i] = new StatArchiveFile(this, archiveNames[i], dump, filters);
+ }
+
+ update(false, autoClose);
+
+ if (this.dump || Boolean.getBoolean("StatArchiveReader.dump")) {
+ this.dump(new PrintWriter(System.out));
+ }
+ }
+
+ /**
+ * Creates a StatArchiveReader that will read the named archive file.
+ * @throws IOException if <code>archiveName</code> could not be opened
+ * read, or closed.
+ */
+ public StatArchiveReader(String archiveName) throws IOException {
+ this(new File[]{new File(archiveName)}, null, false);
+ }
+
+ /**
+ * Returns an array of stat values that match the specified spec.
+ * If nothing matches then an empty array is returned.
+ */
+ public StatValue[] matchSpec(StatSpec spec) {
+ if (spec.getCombineType() == StatSpec.GLOBAL) {
+ StatValue[] allValues = matchSpec(new RawStatSpec(spec));
+ if (allValues.length == 0) {
+ return allValues;
+ } else {
+ ComboValue cv = new ComboValue(allValues);
+ // need to save this in reader's combo value list
+ return new StatValue[]{cv};
+ }
+ } else {
+ List l = new ArrayList();
+ StatArchiveReader.StatArchiveFile[] archives = getArchives();
+ for (int i=0; i < archives.length; i++) {
+ StatArchiveFile f = archives[i];
+ if (spec.archiveMatches(f.getFile())) {
+ f.matchSpec(spec, l);
+ }
+ }
+ StatValue[] result = new StatValue[l.size()];
+ return (StatValue[])l.toArray(result);
+ }
+ }
+
+ /**
+ * Checks to see if any archives have changed since the StatArchiverReader
+ * instance was created or last updated. If an 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.
+ * @return true if update read some new data.
+ * @throws IOException if an archive could not be opened
+ * read, or closed.
+ */
+ public boolean update() throws IOException {
+ return update(true, false);
+ }
+ private boolean update(boolean doReset, boolean autoClose) throws IOException {
+ if (this.closed) {
+ return false;
+ }
+ boolean result = false;
+ StatArchiveReader.StatArchiveFile[] archives = getArchives();
+ for (int i=0; i < archives.length; i++) {
+ StatArchiveFile f = archives[i];
+ if (f.update(doReset)) {
+ result = true;
+ }
+ if (autoClose) {
+ f.close();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns an unmodifiable list of all the {@link ResourceInst}
+ * this reader contains.
+ */
+ public List getResourceInstList() {
+ return new ResourceInstList();
+ }
+
+ public StatArchiveFile[] getArchives() {
+ return this.archives;
+ }
+
+ /**
+ * Closes all archives.
+ */
+ public void close() throws IOException {
+ if (!this.closed) {
+ StatArchiveReader.StatArchiveFile[] archives = getArchives();
+ for (int i=0; i < archives.length; i++) {
+ StatArchiveFile f = archives[i];
+ f.close();
+ }
+ this.closed = true;
+ }
+ }
+
+ private int getMemoryUsed() {
+ int result = 0;
+ StatArchiveReader.StatArchiveFile[] archives = getArchives();
+ for (int i=0; i < archives.length; i++) {
+ StatArchiveFile f = archives[i];
+ result += f.getMemoryUsed();
+ }
+ return result;
+ }
+
+ private void dump(PrintWriter stream) {
+ StatArchiveReader.StatArchiveFile[] archives = getArchives();
+ for (int i=0; i < archives.length; i++) {
+ StatArchiveFile f = archives[i];
+ f.dump(stream);
+ }
+ }
+
+ protected static double bitsToDouble(int type, long bits) {
+ switch (type) {
+ case BOOLEAN_CODE:
+ case BYTE_CODE:
+ case CHAR_CODE:
+ case WCHAR_CODE:
+ case SHORT_CODE:
+ case INT_CODE:
+ case LONG_CODE:
+ return bits;
+ case FLOAT_CODE:
+ return Float.intBitsToFloat((int)bits);
+ case DOUBLE_CODE:
+ return Double.longBitsToDouble(bits);
+ default:
+ throw new InternalGemFireException(LocalizedStrings.StatArchiveReader_UNEXPECTED_TYPECODE_0.toLocalizedString(Integer.valueOf(type)));
+ }
+ }
+
+ /**
+ * Simple utility to read and dump statistic archive.
+ */
+ public static void main(String args[]) throws IOException {
+ String archiveName = null;
+ if (args.length > 1) {
+ System.err.println("Usage: [archiveName]");
+ System.exit(1);
+ } else if (args.length == 1) {
+ archiveName = args[0];
+ } else {
+ archiveName = "statArchive.gfs";
+ }
+ StatArchiveReader reader = new StatArchiveReader(archiveName);
+ System.out.println("DEBUG: memory used = " + reader.getMemoryUsed());
+ reader.close();
+ }
+
+ /**
+ * Wraps an instance of StatSpec but alwasy returns a combine type of NONE.
+ */
+ 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);
+ }
+ }
+
+ private class ResourceInstList extends AbstractList {
+ protected ResourceInstList() {
+ // nothing needed.
+ }
+ @Override
+ public Object get(int idx) {
+ int archiveIdx = 0;
+ StatArchiveReader.StatArchiveFile[] archives = getArchives();
+ for (int i=0; i < archives.length; i++) {
+ StatArchiveFile f = archives[i];
+ if (idx < (archiveIdx + f.resourceInstSize)) {
+ return f.resourceInstTable[idx - archiveIdx];
+ }
+ archiveIdx += f.resourceInstSize;
+ }
+ return null;
+ }
+ @Override
+ public int size() {
+ int result = 0;
+ StatArchiveReader.StatArchiveFile[] archives = getArchives();
+ for (int i=0; i < archives.length; i++) {
+ result += archives[i].resourceInstSize;
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Describes a single statistic.
+ */
+ public static 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;
+
+ protected void dump(PrintWriter stream) {
+ stream.println(" " + name + ": type=" + typeCode + " offset=" + offset
+ + (isCounter? " counter" : "")
+ + " units=" + units
+ + " largerBetter=" + largerBetter
+ + " desc=" + 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;
+ }
+ 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>
+ */
+ public byte getTypeCode() {
+ return this.typeCode;
+ }
+ /**
+ * Returns the name of this statistic.
+ */
+ public String getName() {
+ return this.name;
+ }
+ /**
+ * Returns true if this statistic's value will always increase.
+ */
+ public boolean isCounter() {
+ return this.isCounter;
+ }
+ /**
+ * Returns true if larger values indicate better performance.
+ */
+ public boolean isLargerBetter() {
+ return this.largerBetter;
+ }
+ /**
+ * Returns a string that describes the units this statistic measures.
+ */
+ public String getUnits() {
+ return this.units;
+ }
+ /**
+ * Returns a textual description of this statistic.
+ */
+ public String getDescription() {
+ return this.desc;
+ }
+ /**
+ * Returns the offset of this stat in its type.
+ */
+ public int getOffset() {
+ return this.offset;
+ }
+ }
+
+ public static 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.
+ */
+ public StatValue createTrimmed(long startTime, long endTime);
+ /**
+ * Returns true if value has data that has been trimmed off it
+ * by a start timestamp.
+ */
+ public boolean isTrimmedLeft();
+ /**
+ * Gets the {@link StatArchiveReader.ResourceType type} of the
+ * resources that this value belongs to.
+ */
+ public ResourceType getType();
+ /**
+ * Gets the {@link StatArchiveReader.ResourceInst resources} that this value
+ * belongs to.
+ */
+ 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.
+ */
+ 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.
+ */
+ public long[] getRawAbsoluteTimeStampsWithSecondRes();
+ /**
+ * Returns an array of doubles containing the unfiltered value of this
+ * statistic for each point in time that it was sampled.
+ */
+ public double[] getRawSnapshots();
+ /**
+ * Returns an array of doubles containing the filtered value of this
+ * statistic for each point in time that it was sampled.
+ */
+ public double[] getSnapshots();
+ /**
+ * Returns the number of samples taken of this statistic's value.
+ */
+ public int getSnapshotsSize();
+ /**
+ * Returns the smallest of all the samples taken of this statistic's value.
+ */
+ public double getSnapshotsMinimum();
+ /**
+ * Returns the largest of all the samples taken of this statistic's value.
+ */
+ public double getSnapshotsMaximum();
+ /**
+ * Returns the average of all the samples taken of this statistic's value.
+ */
+ public double getSnapshotsAverage();
+ /**
+ * Returns the standard deviation of all the samples taken of this statistic's value.
+ */
+ public double getSnapshotsStandardDeviation();
+ /**
+ * Returns the most recent value of all the samples taken of this statistic's value.
+ */
+ public double getSnapshotsMostRecent();
+ /**
+ * 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.
+ */
+ 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>
+ */
+ 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.
+ */
+ public StatDescriptor getDescriptor();
+ }
+
+ protected static 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;
+ protected double mostRecent;
+
+ 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 double getSnapshotsMostRecent() {
+ calcStats();
+ return this.mostRecent;
+ }
+ 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(LocalizedStrings.StatArchiveReader_FILTER_VALUE_0_MUST_BE_1_2_OR_3
+ .toLocalizedString(
+ new Object[] {
+ Integer.valueOf(filter),
+ Integer.valueOf(FILTER_NONE),
+ Integer.valueOf(FILTER_PERSEC),
+ Integer.valueOf(FILTER_PERSAMPLE)}));
+ }
+ this.filter = filter;
+ this.statsValid = false;
+ }
+ }
+
+ /**
+ * Calculates each stat given the result of calling getSnapshots
+ */
+ 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;
+ mostRecent = 0.0;
+ } else {
+ min = values[0];
+ max = values[0];
+ mostRecent = values[values.length-1];
+ 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));
+ result.append(" last=") // for bug 42532
+ .append(nf.format(mostRecent));
+ return result.toString();
+ }
+ }
+
+ /**
+ * A ComboValue is a value that is the logical combination of
+ * a set of other stat values.
+ * <p> For now ComboValue has a simple implementation that does not
+ * suppport updates.
+ */
+ private static 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(LocalizedStrings.StatArchiveReader_CANT_COMBINE_VALUES_WITH_DIFFERENT_FILTERS.toLocalizedString());
+ }
+ if (!typeName.equals(this.values[i].getType().getName())) {
+ throw new IllegalArgumentException(LocalizedStrings.StatArchiveReader_CANT_COMBINE_VALUES_WITH_DIFFERENT_TYPES.toLocalizedString());
+ }
+ if (!statName.equals(this.values[i].getDescriptor().getName())) {
+ throw new IllegalArgumentException(LocalizedStrings.StatArchiveReader_CANT_COMBINE_DIFFERENT_STATS.toLocalizedString());
+ }
+ 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() {
+ Set 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;
+ }
+// System.out.println("DEBUG: inst #" + i
+// + " valueTs[" + (j-1) + "]=" + tsToInsert
+// + " found/inserted at"
+// + " mergeTs[" + (ourIdx-1) + "]=" + ourTimeStamps[ourIdx-1]);
+ }
+ }
+// for (int i=0; i < tsCount; i++) {
+// System.out.println("DEBUG: mergedTs[" + i + "]=" + ourTimeStamps[i]);
+// if (i > 0 && ourTimeStamps[i] <= ourTimeStamps[i-1]) {
+// System.out.println("DEBUG: ERROR ts was not greater than previous");
+// }
+// }
+ // Now make one more pass over all the timestamps and make sure they
+ // will all fit into the current merged timestamps in ourTimeStamps
+// boolean changed;
+// do {
+// changed = false;
+// for (int i=0; i < values.length; i++) {
+// valueTimeStamps = values[i].getRawAbsoluteTimeStamps();
+// if (valueTimeStamps.length == 0) {
+// continue;
+// }
+// int ourIdx = 0;
+// for (int j=0; j < valueTimeStamps.length; j++) {
+// while ((ourIdx < (tsCount-1))
+// && !isClosest(valueTimeStamps[j], ourTimeStamps, ourIdx)) {
+// ourIdx++;
+// }
+// if (ourIdx == (tsCount-1)) {
+// // we are at the end so we need to append all our remaining stamps [j..valueTimeStamps.length-1]
+// // and then reappend the Long.MAX_VALUE that
+// // is currently at tsCount-1
+// int numToCopy = valueTimeStamps.length - j;
+// if (tsCount+numToCopy > ourTimeStamps.length) {
+// // grow our timestamp array
+// long[] tmp = new long[tsCount+numToCopy+1];
+// System.arraycopy(ourTimeStamps, 0, tmp, 0, tsCount);
+// ourTimeStamps = tmp;
+// }
+// System.arraycopy(valueTimeStamps, j,
+// ourTimeStamps, ourIdx,
+// numToCopy);
+// tsCount += numToCopy;
+// ourTimeStamps[tsCount-1] = Long.MAX_VALUE;
+// //changed = true;
+// System.out.println("DEBUG: had to add " + numToCopy
+// + " timestamps for inst#" + i + " starting at index " + j + " starting with ts=" + valueTimeStamps[j]);
+// break; // our of the for j loop since we just finished off this guy
+// } else {
+// ourIdx++;
+// }
+// }
+// }
+// } while (changed);
+ // remove the max ts we added
+ tsCount--;
+ {
+ int startIdx = 0;
+ int endIdx = tsCount-1;
+ if (startTime != -1) {
+// for (int i=0; i < tsCount; i++) {
+// if (ourTimeStamps[i] >= startTime) {
+// break;
+// }
+// startIdx++;
+// }
+ Assert.assertTrue(ourTimeStamps[startIdx] >= startTime);
+ }
+ if (endTime != -1) {
+// endIdx = startIdx-1;
+// for (int i=startIdx; i < tsCount; i++) {
+// if (ourTimeStamps[i] >= endTime) {
+// break;
+// }
+// endIdx++;
+// }
+ Assert.assertTrue(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() && valueSnapshots.length > 0) {
+ currentValue = valueSnapshots[0];
+ }
+// System.out.println("DEBUG: inst#" + i + " has " + valueSnapshots.length + " values");
+ for (int j=0; j < valueSnapshots.length; j++) {
+// System.out.println("DEBUG: Doing v with"
+// + " vTs[" + j + "]=" + valueTimeStamps[j]
+// + " at mergeTs[" + curIdx + "]=" + ourTimeStamps[curIdx]);
+ while (!isClosest(valueTimeStamps[j], ourTimeStamps, curIdx)) {
+ if (descriptor.isCounter()) {
+ result[curIdx] += currentValue;
+ }
+// System.out.println("DEBUG: skipping curIdx=" + curIdx
+// + " valueTimeStamps[" + j + "]=" + valueTimeStamps[j]
+// + " ourTimeStamps[" + curIdx + "]=" + ourTimeStamps[curIdx]
+// + " ourTimeStamps[" + (curIdx+1) + "]=" + ourTimeStamps[curIdx+1]);
+
+ 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);
+// if (result[i] > valueDelta) {
+// System.out.println("DEBUG: timeDelta was " + timeDelta + " ms.");
+// System.out.println("DEBUG: valueDelta was " + valueDelta);
+// System.out.println("DEBUG: valueDelta/sec was " + result[i]);
+// }
+ } else {
+ result[i] = valueDelta;
+ }
+ }
+ } else {
+ result = getRawSnapshots();
+ }
+ calcStats(result);
+ return result;
+ }
+ }
+
+ /**
+ * Provides the value series related to a single statistics.
+ */
+ private static class SimpleValue extends AbstractValue {
+ private final ResourceInst resource;
+
+ private boolean useNextBits = false;
+ private long nextBits;
+ private final BitSeries series;
+ private boolean valueChangeNoticed = false;
+
+
+ public StatValue createTrimmed(long startTime, long endTime) {
+ if (startTime == this.startTime && endTime == this.endTime) {
+ return this;
+ } else {
+ return new SimpleValue(this, startTime, endTime);
+ }
+ }
+
+ protected SimpleValue(ResourceInst resource, StatDescriptor sd) {
+ this.resource = resource;
+ if (sd.isCounter()) {
+ this.filter = FILTER_PERSEC;
+ } else {
+ this.filter = FILTER_NONE;
+ }
+ this.descriptor = sd;
+ this.series = new BitSeries();
+ this.statsValid = false;
+ }
+
+ private SimpleValue(SimpleValue in, long startTime, long endTime) {
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.useNextBits = in.useNextBits;
+ this.nextBits = in.nextBits;
+ this.resource = in.resource;
+ this.series = in.series;
+ this.descriptor = in.descriptor;
+ this.filter = in.filter;
+ this.statsValid = false;
+ this.valueChangeNoticed = true;
+ }
+
+ public ResourceType getType() {
+ return this.resource.getType();
+ }
+
+ public ResourceInst[] getResources() {
+ return new ResourceInst[] {this.resource};
+ }
+
+ public boolean isTrimmedLeft() {
+ return getStartIdx() != 0;
+ }
+
+ private int getStartIdx() {
+ int startIdx = 0;
+ if (startTime != -1) {
+ long startTimeStamp = startTime - resource.getTimeBase();
+ long[] timestamps = resource.getAllRawTimeStamps();
+ for (int i=resource.getFirstTimeStampIdx();
+ i < resource.getFirstTimeStampIdx() + series.getSize();
+ i++) {
+ if (timestamps[i] >= startTimeStamp) {
+ break;
+ }
+ startIdx++;
+ }
+ }
+ return startIdx;
+ }
+ private int getEndIdx(int startIdx) {
+ int endIdx = series.getSize()-1;
+ if (endTime != -1) {
+ long endTimeStamp = endTime - resource.getTimeBase();
+ long[] timestamps = resource.getAllRawTimeStamps();
+ endIdx = startIdx-1;
+ for (int i=resource.getFirstTimeStampIdx()+startIdx;
+ i < resource.getFirstTimeStampIdx() + series.getSize();
+ i++) {
+ if (timestamps[i] >= endTimeStamp) {
+ break;
+ }
+ endIdx++;
+ }
+ Assert.assertTrue(endIdx == startIdx-1 || timestamps[endIdx] < endTimeStamp);
+ }
+ return endIdx;
+ }
+ public double[] getSnapshots() {
+ double[] result;
+ int startIdx = getStartIdx();
+ int endIdx = getEndIdx(startIdx);
+ int resultSize=(endIdx-startIdx) + 1;
+
+ if (filter != FILTER_NONE && resultSize > 1) {
+ long[] timestamps = null;
+ if (filter == FILTER_PERSEC) {
+ timestamps = resource.getAllRawTimeStamps();
+ }
+ result = new double[resultSize-1];
+ int tsIdx = resource.getFirstTimeStampIdx()+startIdx;
+ double[] values = series.getValuesEx(descriptor.getTypeCode(), startIdx, resultSize);
+ for (int i=0; i < result.length; i++) {
+ double valueDelta = values[i+1] - values[i];
+ if (filter == FILTER_PERSEC) {
+ double timeDelta = (timestamps[tsIdx+i+1] - timestamps[tsIdx+i]); // millis
+ valueDelta /= (timeDelta / 1000); // per second
+ }
+ result[i] = valueDelta;
+ }
+ } else {
+ result = series.getValuesEx(descriptor.getTypeCode(), startIdx, resultSize);
+ }
+ calcStats(result);
+ return result;
+ }
+
+ public double[] getRawSnapshots() {
+ int startIdx = getStartIdx();
+ int endIdx = getEndIdx(startIdx);
+ int resultSize=(endIdx-startIdx) + 1;
+ return series.getValuesEx(descriptor.getTypeCode(), startIdx, resultSize);
+ }
+
+ public long[] getRawAbsoluteTimeStampsWithSecondRes() {
+ long[] result = getRawAbsoluteTimeStamps();
+ for (int i=0; i < result.length; i++) {
+ result[i] += 500;
+ result[i] /= 1000;
+ result[i] *= 1000;
+ }
+ return result;
+ }
+
+ public long[] getRawAbsoluteTimeStamps() {
+ int startIdx = getStartIdx();
+ int endIdx = getEndIdx(startIdx);
+ int resultSize=(endIdx-startIdx) + 1;
+ if (resultSize <= 0) {
+ return new long[0];
+ } else {
+ long[] result = new long[resultSize];
+ long[] timestamps = resource.getAllRawTimeStamps();
+ int tsIdx = resource.getFirstTimeStampIdx()+startIdx;
+ long base = resource.getTimeBase();
+ for (int i=0; i < resultSize; i++) {
+ result[i] = base + timestamps[tsIdx+i];
+ }
+ return result;
+ }
+ }
+
+ public boolean hasValueChanged() {
+ if (valueChangeNoticed) {
+ valueChangeNoticed = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ protected int getMemoryUsed() {
+ int result = 0;
+ if (series != null) {
+ result += series.getMemoryUsed();
+ }
+ return result;
+ }
+
+ protected void dump(PrintWriter stream) {
+ calcStats();
+ stream.print(" " + descriptor.getName() + "=");
+ stream.print("[size=" + getSnapshotsSize()
+ + " min=" + nf.format(min)
+ + " max=" + nf.format(max)
+ + " avg=" + nf.format(avg)
+ + " stddev=" + nf.format(stddev) + "]");
+ if (Boolean.getBoolean("StatArchiveReader.dumpall")) {
+ series.dump(stream);
+ } else {
+ stream.println();
+ }
+ }
+
+ protected void shrink() {
+ this.series.shrink();
+ }
+
+ protected void initialValue(long v) {
+ this.series.initialBits(v);
+ }
+ protected void prepareNextBits(long bits) {
+ useNextBits = true;
+ nextBits = bits;
+ }
+ protected void addSample() {
+ statsValid = false;
+ if (useNextBits) {
+ useNextBits = false;
+ series.addBits(nextBits);
+ valueChangeNoticed = true;
+ } else {
+ series.addBits(0);
+ }
+ }
+ }
+
+ private static 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;
+
+ 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);
+ }
+ }
+ }
+ }
+ }
+
+ private static abstract class BitNonZeroInterval extends BitInterval {
+ @Override
+ int getMemoryUsed() {
+ return super.getMemoryUsed() + 4;
+ }
+ abstract long getBits();
+ abstract long getInterval();
+
+ @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 base = getBits();
+ long interval = getInterval();
+ base += skipCount * interval;
+ for (int i=0; i < fillcount; i++) {
+ values[valueOffset+i] = bitsToDouble(typeCode, base);
+ base += interval;
+ }
+ return fillcount;
+ }
+
+ @Override
+ void dump(PrintWriter stream) {
+ stream.print(getBits());
+ if (count > 1) {
+ long interval = getInterval();
+ if (interval != 0) {
+ stream.print("+=" + interval);
+ }
+ stream.print("r" + count);
+ }
+ }
+ BitNonZeroInterval(int count) {
+ this.count = count;
+ }
+ @Override
+ boolean attemptAdd(long addBits, long addInterval, int addCount) {
+ // addCount >= 2; count >= 2
+ if (addInterval == getInterval()) {
+ if (addBits == (getBits() + (addInterval * (count-1)))) {
+ count += addCount;
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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 interval can produce
+ if (fillcount > maxCount) {
+ fillcount = maxCount;
+ }
+ double value = bitsToDouble(typeCode, getBits());
+ for (int i=0; i < fillcount; i++) {
+ values[valueOffset+i] = value;
+ }
+ return fillcount;
+ }
+
+ @Override
+ void dump(PrintWriter stream) {
+ stream.print(getBits());
+ if (count > 1) {
+ stream.print("r" + count);
+ }
+ }
+ BitZeroInterval(int count) {
+ this.count = count;
+ }
+ @Override
+ boolean attemptAdd(long addBits, long addInterval, int addCount) {
+ // addCount >= 2; count >= 2
+ if (addInterval == 0 && addBits == getBits()) {
+ count += addCount;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private static class BitZeroIntInterval extends BitZeroInterval {
+ int bits;
+
+ @Override
+ int getMemoryUsed() {
+ return super.getMemoryUsed() + 4;
+ }
+ @Override
+ long getBits() {
+ return bits;
+ }
+ BitZeroIntInterval(int bits, int count) {
+ super(count);
+ this.bits = bits;
+ }
+ }
+
+ private static class BitZeroLongInterval extends BitZeroInterval {
+ long bits;
+
+ @Override
+ int getMemoryUsed() {
+ return super.getMemoryUsed() + 8;
+ }
+ @Override
+ long getBits() {
+ return bits;
+ }
+ BitZeroLongInterval(long bits, int count) {
+ super(count);
+ this.bits = bits;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ private static 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 InternalGemFireException(LocalizedStrings.StatArchiveReader_GETVALUESEX_DIDNT_FILL_THE_LAST_0_ENTRIES_OF_ITS_RESULT.toLocalizedString(Integer.valueOf(resultSize-idx)));
+ }
+ 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;
+ }
+ }
+ }
+ }
+
+ private static 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;
+ }
+ }
+
+ /**
+ * Defines a statistic resource type. Each resource instance must be
+ * of a single type. The type defines what statistics each instance
+ * of it will support. The type also has a description of itself.
+ */
+ public static class ResourceType {
+ private boolean loaded;
+// private final int id;
+ private final String name;
+ private String desc;
+ private final StatDescriptor[] stats;
+ private Map descriptorMap;
+
+ public void dump(PrintWriter stream) {
+ if (loaded) {
+ stream.println(name + ": " + desc);
+ for (int i=0; i < stats.length; i++) {
+ stats[i].dump(stream);
+ }
+ }
+ }
+
+ 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;
+ }
+
+ 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();
+ }
+ 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.
+ */
+ 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;
+ }
+
+ 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.
+ */
+ public String getName() {
+ return this.name;
+ }
+ /**
+ * Returns an array of descriptors for each statistic this resource
+ * type supports.
+ */
+ 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.
+ */
+ public String getDescription() {
+ return this.desc;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ResourceType other = (ResourceType) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+ }
+
+ /**
+ * Describes some global information about the archive.
+ */
+ public static 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;
+
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ public long getSystemId() {
+ return this.systemId;
+ }
+ /**
+ * Returns a string describing the operating system the archive was
+ * written on.
+ */
+ public String getOs() {
+ return this.os;
+ }
+ /**
+ * Returns a string describing the machine the archive was
+ * written on.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ public int getArchiveFormatVersion() {
+ return this.archiveVersion;
+ }
+ /**
+ * Returns a string describing the system that this archive recorded.
+ */
+ 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.
+ */
+ 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();
+ }
+
+ 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);
+ if (archive != null) {
+ stream.println("systemStartDate=" + archive.formatTimeMillis(systemStartTimeStamp));
+ }
+ stream.println("systemId=" + systemId);
+ stream.println("productVersion=" + productVersion);
+ stream.println("osInfo=" + os);
+ stream.println("machineInfo=" + machine);
+ }
+ }
+
+ /**
+ * Defines a single instance of a resource type.
+ */
+ public static 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;
+ 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.
+ */
+ protected int getMemoryUsed() {
+ int result = 0;
+ if (values != null) {
+ for (int i=0; i < values.length; i++) {
+ result += this.values[i].getMemoryUsed();
+ }
+ }
+ return result;
+ }
+
+ public StatArchiveReader 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)
+ .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.
+ */
+ public int getSampleCount() {
+ if (active) {
+ return archive.getTimeStamps().getSize() - firstTSidx;
+ } else {
+ return (lastTSidx+1) - firstTSidx;
+ }
+ }
+
+ public StatArchiveFile getArchive() {
+ return this.archive;
+ }
+
+ 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);
+ }
+ }
+
+ 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.assertTrue(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]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ 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.
+ */
+ 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;
+ }
+ }
+
+ 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.
+ */
+ 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;
+ }
+ }
+
+ protected int getFirstTimeStampIdx() {
+ return this.firstTSidx;
+ }
+ protected long[] getAllRawTimeStamps() {
+ return archive.getTimeStamps().getRawTimeStamps();
+ }
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ public String getName() {
+ return this.name;
+ }
+ /**
+ * Returns the id of this instance.
+ */
+ 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.
+ */
+ public long getFirstTimeMillis() {
+ return archive.getTimeStamps().getMilliTimeStamp(firstTSidx);
+ }
+ /**
+ * Returns resource type of this instance.
+ */
+ 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.
+ */
+ 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();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (id ^ (id >>> 32));
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+
<TRUNCATED>