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 2008 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.io.*;
31 import java.beans.*;
32 import java.math.BigInteger;
33 
34 /**
35  * A {@code long} value aggregated by the DTrace {@code stddev()} action.
36  * <p>
37  * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
38  *
39  * @see Aggregation
40  * @author Tom Erickson
41  */
42 public final class StddevValue extends AbstractAggregationValue {
43     static final long serialVersionUID = 6409878160513885375L;
44 
45     /** @serial */
46     private final long total;
47     /** @serial */
48     private final long count;
49     /** @serial */
50     private final BigInteger totalSquares;
51 
52     static {
53 	try {
54 	    BeanInfo info = Introspector.getBeanInfo(StddevValue.class);
55 	    PersistenceDelegate persistenceDelegate =
56 		    new DefaultPersistenceDelegate(
57 		    new String[] {"value", "total", "count", "totalSquares"})
58 	    {
59 		@Override
60 		protected Expression
61 		instantiate(Object oldInstance, Encoder out)
62 		{
63 		    StddevValue stddev = (StddevValue)oldInstance;
64 		    return new Expression(oldInstance, oldInstance.getClass(),
65 			    "new", new Object[] {
66 			    stddev.getValue().longValue(),
67 			    stddev.getTotal(), stddev.getCount(),
68 			    stddev.getTotalSquares().toString() });
69 		}
70 	    };
71 	    BeanDescriptor d = info.getBeanDescriptor();
72 	    d.setValue("persistenceDelegate", persistenceDelegate);
73 	} catch (IntrospectionException e) {
74 	    System.out.println(e);
75 	}
76     }
77 
78     // ported from dt_sqrt_128 in lib/libdtrace/common/dt_consume.c
79     private static long
squareRoot128(BigInteger n)80     squareRoot128(BigInteger n)
81     {
82 	long result = 0;
83 	BigInteger diff = BigInteger.valueOf(0);
84 	BigInteger nextTry = BigInteger.valueOf(0);
85 	int bitPairs = (n.bitLength() / 2);
86 	int bitPos = (bitPairs * 2) + 1;
87 	int nextTwoBits;
88 
89 	for (int i = 0; i <= bitPairs; i++) {
90 	    // Bring down the next pair of bits.
91 	    nextTwoBits = n.testBit(bitPos)
92 		    ? (n.testBit(bitPos - 1) ? 0x3 : 0x2)
93 		    : (n.testBit(bitPos - 1) ? 0x1 : 0x0);
94 
95 	    diff = diff.shiftLeft(2);
96 	    diff = diff.add(BigInteger.valueOf(nextTwoBits));
97 
98 	    // nextTry = R << 2 + 1
99 	    nextTry = BigInteger.valueOf(result);
100 	    nextTry = nextTry.shiftLeft(2);
101 	    nextTry = nextTry.setBit(0);
102 
103 	    result <<= 1;
104 	    if (nextTry.compareTo(diff) <= 0) {
105 		diff = diff.subtract(nextTry);
106 		result++;
107 	    }
108 
109 	    bitPos -= 2;
110 	}
111 
112 	return (result);
113     }
114 
115     // ported from dt_stddev in lib/libdtrace/common/dt_consume.c
116     private static long
standardDeviation(long stddevCount, long stddevTotal, BigInteger stddevTotalSquares)117     standardDeviation(long stddevCount, long stddevTotal,
118 	    BigInteger stddevTotalSquares)
119     {
120 	BigInteger averageOfSquares = stddevTotalSquares.divide(
121 		BigInteger.valueOf(stddevCount));
122 	long avg = (stddevTotal / stddevCount);
123 	if (avg < 0) {
124 	    avg = -avg;
125 	}
126 	BigInteger squareOfAverage = BigInteger.valueOf(avg);
127 	squareOfAverage = squareOfAverage.pow(2);
128 	BigInteger stddev = averageOfSquares.subtract(squareOfAverage);
129 	return squareRoot128(stddev);
130     }
131 
132     /*
133      * Called by native code.
134      */
135     private
StddevValue(long stddevCount, long stddevTotal, BigInteger stddevTotalSquares)136     StddevValue(long stddevCount, long stddevTotal,
137 	    BigInteger stddevTotalSquares)
138     {
139 	super(stddevCount == 0 ? 0 : standardDeviation(stddevCount,
140 		stddevTotal, stddevTotalSquares));
141 	total = stddevTotal;
142 	count = stddevCount;
143 	totalSquares = stddevTotalSquares;
144 	if (totalSquares == null) {
145 	    throw new NullPointerException("totalSquares is null");
146 	}
147 	if (count < 0) {
148 	    throw new IllegalArgumentException("count is negative");
149 	}
150     }
151 
152     /**
153      * Creates a value aggregated by the DTrace {@code stddev()} action.
154      * Supports XML persistence.
155      *
156      * @param v standard deviation
157      * @param stddevTotal sum total of all values included in the standard
158      * deviation
159      * @param stddevCount number of values included in the standard
160      * deviation
161      * @param stddevTotalSquaresString decimal string representation of
162      * the 128-bit sum total of the squares of all values included in
163      * the standard deviation
164      * @throws IllegalArgumentException if the given count is negative
165      * or if the given standard deviation is not the value expected for
166      * the given total, total of squares, and count
167      * @throws NumberFormatException if the given total squares is not a
168      * valid integer representation
169      */
170     public
StddevValue(long v, long stddevTotal, long stddevCount, String stddevTotalSquaresString)171     StddevValue(long v, long stddevTotal, long stddevCount,
172 	    String stddevTotalSquaresString)
173     {
174 	super(v);
175 	total = stddevTotal;
176 	count = stddevCount;
177 	totalSquares = new BigInteger(stddevTotalSquaresString);
178 	validate();
179     }
180 
181     private final void
validate()182     validate()
183     {
184 	if (count < 0) {
185 	    throw new IllegalArgumentException("count is negative");
186 	}
187 	long stddev = super.getValue().longValue();
188 	if (count == 0) {
189 	    if (stddev != 0) {
190 		throw new IllegalArgumentException(
191 			"count of values is zero, stddev is non-zero (" +
192 			stddev + ")");
193 	    }
194 	} else {
195 	    if (stddev != standardDeviation(count, total, totalSquares)) {
196 		throw new IllegalArgumentException(
197 			getValue().toString() + " is not the expected " +
198 			"standard deviation of total " + total + ", count " +
199 			count + ", and total squares " + totalSquares);
200 	    }
201 	}
202     }
203 
204     // Needed to support XML persistence since XMLDecoder cannot find
205     // the public method of the non-public superclass.
206 
207     /**
208      * Gets the standard deviation of the aggregated values.
209      *
210      * @return standard deviation of the aggregated values
211      */
212     public Long
getValue()213     getValue()
214     {
215 	return (Long)super.getValue();
216     }
217 
218     /**
219      * Gets the sum total of the aggregated values.
220      *
221      * @return the sum total of the aggregated values
222      */
223     public long
getTotal()224     getTotal()
225     {
226 	return total;
227     }
228 
229     /**
230      * Gets the number of aggregated values included in the standard
231      * deviation.
232      *
233      * @return the number of aggregated values included in the standard
234      * deviation
235      */
236     public long
getCount()237     getCount()
238     {
239 	return count;
240     }
241 
242     /**
243      * Gets the sum total of the squares of the aggregated values.
244      *
245      * @return the sum total of the squares of the aggregated values
246      */
247     public BigInteger
getTotalSquares()248     getTotalSquares()
249     {
250 	return totalSquares;
251     }
252 
253     private void
readObject(ObjectInputStream s)254     readObject(ObjectInputStream s)
255             throws IOException, ClassNotFoundException
256     {
257 	s.defaultReadObject();
258 	// check invariants
259 	try {
260 	    validate();
261 	} catch (Exception e) {
262 	    InvalidObjectException x = new InvalidObjectException(
263 		    e.getMessage());
264 	    x.initCause(e);
265 	    throw x;
266 	}
267     }
268 }
269