1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * ident	"%Z%%M%	%I%	%E% SMI"
27  */
28 package org.opensolaris.os.dtrace;
29 
30 import java.util.*;
31 import java.io.*;
32 import java.beans.*;
33 
34 /**
35  * A power-of-two logarithmic frequency distribution aggregated by the
36  * DTrace {@code quantize()} action.  Aggregated values fall into
37  * consecutive ranges, each twice as large as the previous range.  Each
38  * range, known as a bucket, begins at two to the power of <i>n</i> and
39  * ends at one less than the beginning of the next bucket, two to the
40  * power of <i>n + 1</i>.  The zero bucket is the degenerate case and
41  * holds the frequency of the base value zero.  For example, the first
42  * bucket after 0 starts at 1 (2 to the power of 0) and ends at 1 (one
43  * less than 2 to the power of 1).  The next bucket starts at 2 (2 to
44  * the power of 1) and ends at 3 (one less than 2 to the power of 2).
45  * Each bucket frequency is incremented for each aggregated value that
46  * falls into its range.  Buckets are typically identified by their
47  * lower bound: 1, 2, 4, 8, etc.  Mirroring these are buckets with
48  * negative ranges: -1, -2, -4, -8, etc.  The range of an entire {@code
49  * LogDistribution} is (<code>-2<sup>63</sup> ..
50  * 2<sup>63</sup></code>).
51  * <p>
52  * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
53  *
54  * @see LinearDistribution
55  * @see Aggregation
56  *
57  * @author Tom Erickson
58  */
59 public final class LogDistribution extends Distribution
60         implements Serializable, Comparable <LogDistribution>
61 {
62     static final long serialVersionUID = -1279719751212721961L;
63 
64     static final int ZERO_BUCKET_INDEX = 63;
65 
66     static {
67 	try {
68 	    BeanInfo info = Introspector.getBeanInfo(LogDistribution.class);
69 	    PersistenceDelegate persistenceDelegate =
70 		    new DefaultPersistenceDelegate(
71 		    new String[] {"buckets"});
72 	    BeanDescriptor d = info.getBeanDescriptor();
73 	    d.setValue("persistenceDelegate", persistenceDelegate);
74 	} catch (IntrospectionException e) {
75 	    System.out.println(e);
76 	}
77     }
78 
79     /**
80      * Called by native C code
81      */
82     private
LogDistribution(long[] buckets)83     LogDistribution(long[] buckets)
84     {
85 	super(0, 2, buckets); // initializes using base 0, power of 2
86     }
87 
88     /**
89      * Creates a logarithmic distribution with the given frequencies.
90      * Supports XML persistence.
91      *
92      * @param frequencies list of frequencies in bucket ranges bounded
93      * by consucutive powers of two
94      * @throws NullPointerException if {@code frequencies} is {@code
95      * null}
96      * @throws IllegalArgumentException if any bucket does not have the
97      * expected range (bounded by consecutive powers of two)
98      */
99     public
LogDistribution(List <Bucket> frequencies)100     LogDistribution(List <Bucket> frequencies)
101     {
102 	super(frequencies);
103 	initialize();
104     }
105 
106     /**
107      * Gets a two element array: the first elelemt is the range minimum
108      * (inclusive), the second element is the range maximum (inclusive).
109      */
110     @Override
111     long[]
getBucketRange(int i, int len, long base, long constant)112     getBucketRange(int i, int len, long base, long constant)
113     {
114 	long min = LocalConsumer._quantizeBucket(i);
115 	long max = (LocalConsumer._quantizeBucket(i + 1) - 1);
116 
117 	long[] range = new long[] {min, max};
118 	return range;
119     }
120 
121     @Override
122     long[]
getBucketRange(int i, int len)123     getBucketRange(int i, int len)
124     {
125 	return getBucketRange(i, len, 0, 2);
126     }
127 
128     public Number
getValue()129     getValue()
130     {
131 	double total = 0;
132 	List <Distribution.Bucket> buckets = getBuckets();
133 	for (Distribution.Bucket bucket : buckets) {
134 	    total += ((double)bucket.getFrequency() * (double)bucket.getMin());
135 	}
136 	return (new Double(total));
137     }
138 
139     private long
getZeroBucketValue()140     getZeroBucketValue()
141     {
142 	Distribution.Bucket b = get(ZERO_BUCKET_INDEX);
143 	return b.getFrequency();
144     }
145 
146     /**
147      * Compares the {@code double} values of {@link #getValue()} for
148      * overall magnitude, and if those are equal, compares the
149      * frequencies at the zero bucket (the bucket whose range has a
150      * minimum and maximum value of zero).
151      */
152     public int
compareTo(LogDistribution d)153     compareTo(LogDistribution d)
154     {
155 	Number v1 = getValue();
156 	Number v2 = d.getValue();
157 	double d1 = v1.doubleValue();
158 	double d2 = v2.doubleValue();
159 	int cmp = (d1 < d2 ? -1 : (d1 > d2 ? 1 : 0));
160 	if (cmp == 0) {
161 	    long z1 = getZeroBucketValue();
162 	    long z2 = d.getZeroBucketValue();
163 	    cmp = (z1 < z2 ? -1 : (z1 > z2 ? 1 : 0));
164 	}
165 	return (cmp);
166     }
167 
168     private void
readObject(ObjectInputStream s)169     readObject(ObjectInputStream s)
170             throws IOException, ClassNotFoundException
171     {
172 	s.defaultReadObject();
173 	try {
174 	    initialize();
175 	} catch (Exception e) {
176 	    InvalidObjectException x = new InvalidObjectException(
177 		    e.getMessage());
178 	    x.initCause(e);
179 	    throw x;
180 	}
181     }
182 }
183