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 linear frequency distribution aggregated by the DTrace {@code 36 * lquantize()} action. Aggregated values fall into consecutive ranges 37 * bounded by the step parameter of the {@code lquantize()} action. 38 * Each range, known as a bucket, begins at the {@code lquantize()} 39 * lower bound, or base, plus a multiple of the {@code lquantize()} 40 * step, unless it is the first bucket, which is the frequency of all 41 * aggregated values less than the base. The last bucket counts all 42 * aggregated values greater than or equal to the {@code lquantize()} 43 * upper bound. For example 44 * <pre> {@code @ = lquantize(n, 0, 100, 10);}</pre> 45 * results in a distribution with a base of 0, an upper bound of 100, 46 * and a step of 10. It has twelve buckets starting with {@code n < 0} 47 * and ending with {@code n >= 100}. The buckets in between are {@code 48 * 0 .. 9}, {@code 10 .. 19}, etc. 49 * <p> 50 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 51 * 52 * @see LogDistribution 53 * @see Aggregation 54 * 55 * @author Tom Erickson 56 */ 57 public final class LinearDistribution extends Distribution 58 implements Serializable, Comparable <LinearDistribution> 59 { 60 static final long serialVersionUID = 7100080045858770132L; 61 62 /** @serial */ 63 private long base; 64 /** @serial */ 65 private long step; 66 67 static { 68 try { 69 BeanInfo info = Introspector.getBeanInfo(LinearDistribution.class); 70 PersistenceDelegate persistenceDelegate = 71 new DefaultPersistenceDelegate( 72 new String[] {"base", "step", "buckets" }); 73 BeanDescriptor d = info.getBeanDescriptor(); 74 d.setValue("persistenceDelegate", persistenceDelegate); 75 } catch (IntrospectionException e) { 76 System.out.println(e); 77 } 78 } 79 80 /** 81 * Called by native C code 82 */ 83 private 84 LinearDistribution(long lowerBound, long frequencyStep, 85 long[] frequencies) 86 { 87 // initializes using lowerBound and frequencyStep 88 super(lowerBound, frequencyStep, frequencies); 89 base = lowerBound; 90 step = frequencyStep; 91 } 92 93 /** 94 * Creates a linear distribution with the given base, step, and 95 * frequencies. Supports XML persistence. 96 * 97 * @param lowerBound also known as the <i>base</i>; the minimum of 98 * the second bucket in this distribution (the first bucket contains 99 * the frequency of everything lower than the base) 100 * @param bucketStep the distance between the lower bound of one 101 * bucket and the lower bound of the next consecutive bucket 102 * (excluding the first bucket) 103 * @param frequencies list of frequencies in each bucket range 104 * @throws NullPointerException if {@code frequencies} is {@code 105 * null} 106 * @throws IllegalArgumentException if the given step is less than 107 * one, or if any bucket does not have the expected range 108 * (consecutive steps) 109 */ 110 public 111 LinearDistribution(long lowerBound, long bucketStep, 112 List <Bucket> frequencies) 113 { 114 super(frequencies); // makes defensive copy 115 base = lowerBound; 116 step = bucketStep; 117 initialize(); // checks class invariants, calculates total 118 if (step < 1) { 119 throw new IllegalArgumentException("step is less than one"); 120 } 121 } 122 123 /** 124 * Gets a two element array: the first elelemt is the range minimum 125 * (inclusive), the second element is the range maximum (inclusive). 126 */ 127 @Override 128 long[] 129 getBucketRange(int i, int len, long base, long step) 130 { 131 long min; 132 long max; 133 if (i == 0) { 134 // first bucket is everything less than the base 135 min = Long.MIN_VALUE; 136 } else { 137 min = (base + ((i - 1) * step)); 138 } 139 140 if (i == (len - 1)) { 141 // last bucket is everything greater than or equal to 142 // the upper bound 143 max = Long.MAX_VALUE; 144 } else { 145 max = ((base + (i * step)) - 1); 146 } 147 148 long[] range = new long[] {min, max}; 149 return range; 150 } 151 152 @Override 153 long[] 154 getBucketRange(int i, int len) 155 { 156 return getBucketRange(i, len, base, step); 157 } 158 159 /** 160 * Gets the lower bound of this distribution. In a linear 161 * distribution, the first bucket holds the frequency of all values 162 * less than the base, so the base is the minimum of the second 163 * bucket's range. 164 * 165 * @return the lower bound of this distribution 166 */ 167 public long 168 getBase() 169 { 170 return base; 171 } 172 173 /** 174 * Gets the difference between the lower bounds of consecutive 175 * buckets after the first. 176 * 177 * @return the step between the lower bounds of consecutive buckets 178 * after the first 179 */ 180 public long 181 getStep() 182 { 183 return step; 184 } 185 186 public Number 187 getValue() 188 { 189 double total = 0; 190 List <Distribution.Bucket> buckets = getBuckets(); 191 int len = buckets.size(); 192 Distribution.Bucket bucket; 193 194 if (len > 0) { 195 bucket = buckets.get(0); 196 total = (double)bucket.getFrequency() * (double)(getBase() - 1); 197 } 198 for (int i = 1; i < (len - 1); ++i) { 199 bucket = buckets.get(i); 200 total += (double)bucket.getFrequency() * (double)bucket.getMin(); 201 } 202 if (len > 1) { 203 bucket = buckets.get(len - 1); 204 // There doesn't seem to be any reason to add one to the 205 // minimum of the last bucket range, but that's how it's 206 // implemented in libdtrace dt_aggregate.c. 207 total += (double)bucket.getFrequency() * 208 (double)(bucket.getMin() + 1); 209 } 210 return (new Double(total)); 211 } 212 213 private long 214 getZeroBucketValue() 215 { 216 for (Distribution.Bucket b : this) { 217 if (b.getMin() == 0) { 218 return b.getFrequency(); 219 } 220 } 221 return 0; 222 } 223 224 /** 225 * Compares the {@code double} values of {@link #getValue()} for 226 * overall magnitude, and if those are equal, compares the 227 * frequencies at zero if the distributions include a bucket whose 228 * range has a minimum of zero. 229 */ 230 public int 231 compareTo(LinearDistribution d) 232 { 233 Number v1 = getValue(); 234 Number v2 = d.getValue(); 235 double d1 = v1.doubleValue(); 236 double d2 = v2.doubleValue(); 237 int cmp = (d1 < d2 ? -1 : (d1 > d2 ? 1 : 0)); 238 if (cmp == 0) { 239 long z1 = getZeroBucketValue(); 240 long z2 = d.getZeroBucketValue(); 241 cmp = (z1 < z2 ? -1 : (z1 > z2 ? 1 : 0)); 242 } 243 return (cmp); 244 } 245 246 private void 247 readObject(ObjectInputStream s) 248 throws IOException, ClassNotFoundException 249 { 250 s.defaultReadObject(); 251 try { 252 initialize(); 253 } catch (Exception e) { 254 InvalidObjectException x = new InvalidObjectException( 255 e.getMessage()); 256 x.initCause(e); 257 throw x; 258 } 259 if (step < 1) { 260 throw new InvalidObjectException("step is less than one"); 261 } 262 } 263 264 /** 265 * Gets a string representation of this linear distribution useful 266 * for logging and not intended for display. The exact details of 267 * the representation are unspecified and subject to change, but the 268 * following format may be regarded as typical: 269 * <pre><code> 270 * class-name[property1 = value1, property2 = value2] 271 * </code></pre> 272 */ 273 public String 274 toString() 275 { 276 StringBuilder buf = new StringBuilder(); 277 buf.append(LinearDistribution.class.toString()); 278 buf.append("[base = "); 279 buf.append(getBase()); 280 buf.append(", step = "); 281 buf.append(getStep()); 282 buf.append(", buckets = "); 283 List <Bucket> list = getDisplayRange(); 284 if (list.isEmpty()) { 285 buf.append("<empty>"); 286 } else { 287 buf.append(Arrays.toString(getDisplayRange().toArray())); 288 } 289 buf.append(", total = "); 290 buf.append(getTotal()); 291 buf.append(']'); 292 return buf.toString(); 293 } 294 } 295