/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * ident "%Z%%M% %I% %E% SMI" */ package org.opensolaris.os.dtrace; import java.util.*; import java.io.*; import java.beans.*; /** * A linear frequency distribution aggregated by the DTrace {@code * lquantize()} action. Aggregated values fall into consecutive ranges * bounded by the step parameter of the {@code lquantize()} action. * Each range, known as a bucket, begins at the {@code lquantize()} * lower bound, or base, plus a multiple of the {@code lquantize()} * step, unless it is the first bucket, which is the frequency of all * aggregated values less than the base. The last bucket counts all * aggregated values greater than or equal to the {@code lquantize()} * upper bound. For example *
		{@code @ = lquantize(n, 0, 100, 10);}
* results in a distribution with a base of 0, an upper bound of 100, * and a step of 10. It has twelve buckets starting with {@code n < 0} * and ending with {@code n >= 100}. The buckets in between are {@code * 0 .. 9}, {@code 10 .. 19}, etc. *

* Immutable. Supports persistence using {@link java.beans.XMLEncoder}. * * @see LogDistribution * @see Aggregation * * @author Tom Erickson */ public final class LinearDistribution extends Distribution implements Serializable, Comparable { static final long serialVersionUID = 7100080045858770132L; /** @serial */ private long base; /** @serial */ private long step; static { try { BeanInfo info = Introspector.getBeanInfo(LinearDistribution.class); PersistenceDelegate persistenceDelegate = new DefaultPersistenceDelegate( new String[] {"base", "step", "buckets" }); BeanDescriptor d = info.getBeanDescriptor(); d.setValue("persistenceDelegate", persistenceDelegate); } catch (IntrospectionException e) { System.out.println(e); } } /** * Called by native C code */ private LinearDistribution(long lowerBound, long frequencyStep, long[] frequencies) { // initializes using lowerBound and frequencyStep super(lowerBound, frequencyStep, frequencies); base = lowerBound; step = frequencyStep; } /** * Creates a linear distribution with the given base, step, and * frequencies. Supports XML persistence. * * @param lowerBound also known as the base; the minimum of * the second bucket in this distribution (the first bucket contains * the frequency of everything lower than the base) * @param bucketStep the distance between the lower bound of one * bucket and the lower bound of the next consecutive bucket * (excluding the first bucket) * @param frequencies list of frequencies in each bucket range * @throws NullPointerException if {@code frequencies} is {@code * null} * @throws IllegalArgumentException if the given step is less than * one, or if any bucket does not have the expected range * (consecutive steps) */ public LinearDistribution(long lowerBound, long bucketStep, List frequencies) { super(frequencies); // makes defensive copy base = lowerBound; step = bucketStep; initialize(); // checks class invariants, calculates total if (step < 1) { throw new IllegalArgumentException("step is less than one"); } } /** * Gets a two element array: the first elelemt is the range minimum * (inclusive), the second element is the range maximum (inclusive). */ @Override long[] getBucketRange(int i, int len, long base, long step) { long min; long max; if (i == 0) { // first bucket is everything less than the base min = Long.MIN_VALUE; } else { min = (base + ((i - 1) * step)); } if (i == (len - 1)) { // last bucket is everything greater than or equal to // the upper bound max = Long.MAX_VALUE; } else { max = ((base + (i * step)) - 1); } long[] range = new long[] {min, max}; return range; } @Override long[] getBucketRange(int i, int len) { return getBucketRange(i, len, base, step); } /** * Gets the lower bound of this distribution. In a linear * distribution, the first bucket holds the frequency of all values * less than the base, so the base is the minimum of the second * bucket's range. * * @return the lower bound of this distribution */ public long getBase() { return base; } /** * Gets the difference between the lower bounds of consecutive * buckets after the first. * * @return the step between the lower bounds of consecutive buckets * after the first */ public long getStep() { return step; } public Number getValue() { double total = 0; List buckets = getBuckets(); int len = buckets.size(); Distribution.Bucket bucket; if (len > 0) { bucket = buckets.get(0); total = (double)bucket.getFrequency() * (double)(getBase() - 1); } for (int i = 1; i < (len - 1); ++i) { bucket = buckets.get(i); total += (double)bucket.getFrequency() * (double)bucket.getMin(); } if (len > 1) { bucket = buckets.get(len - 1); // There doesn't seem to be any reason to add one to the // minimum of the last bucket range, but that's how it's // implemented in libdtrace dt_aggregate.c. total += (double)bucket.getFrequency() * (double)(bucket.getMin() + 1); } return (new Double(total)); } private long getZeroBucketValue() { for (Distribution.Bucket b : this) { if (b.getMin() == 0) { return b.getFrequency(); } } return 0; } /** * Compares the {@code double} values of {@link #getValue()} for * overall magnitude, and if those are equal, compares the * frequencies at zero if the distributions include a bucket whose * range has a minimum of zero. */ public int compareTo(LinearDistribution d) { Number v1 = getValue(); Number v2 = d.getValue(); double d1 = v1.doubleValue(); double d2 = v2.doubleValue(); int cmp = (d1 < d2 ? -1 : (d1 > d2 ? 1 : 0)); if (cmp == 0) { long z1 = getZeroBucketValue(); long z2 = d.getZeroBucketValue(); cmp = (z1 < z2 ? -1 : (z1 > z2 ? 1 : 0)); } return (cmp); } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); try { initialize(); } catch (Exception e) { InvalidObjectException x = new InvalidObjectException( e.getMessage()); x.initCause(e); throw x; } if (step < 1) { throw new InvalidObjectException("step is less than one"); } } /** * Gets a string representation of this linear distribution useful * for logging and not intended for display. The exact details of * the representation are unspecified and subject to change, but the * following format may be regarded as typical: *


     * class-name[property1 = value1, property2 = value2]
     * 
*/ public String toString() { StringBuilder buf = new StringBuilder(); buf.append(LinearDistribution.class.toString()); buf.append("[base = "); buf.append(getBase()); buf.append(", step = "); buf.append(getStep()); buf.append(", buckets = "); List list = getDisplayRange(); if (list.isEmpty()) { buf.append(""); } else { buf.append(Arrays.toString(getDisplayRange().toArray())); } buf.append(", total = "); buf.append(getTotal()); buf.append(']'); return buf.toString(); } }