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 2006 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.util.Arrays; 32 import java.beans.*; 33 34 /** 35 * A traced D primitive generated by a DTrace action such as {@code 36 * trace()} or {@code tracemem()}, or else an element in a composite 37 * value generated by DTrace. 38 * <p> 39 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 40 * 41 * @author Tom Erickson 42 */ 43 public final class ScalarRecord implements ValueRecord, Serializable { 44 static final long serialVersionUID = -34046471695050108L; 45 static final int RAW_BYTES_INDENT = 5; 46 47 static { 48 try { 49 BeanInfo info = Introspector.getBeanInfo(ScalarRecord.class); 50 PersistenceDelegate persistenceDelegate = 51 new DefaultPersistenceDelegate( 52 new String[] {"value"}) 53 { 54 /* 55 * Need to prevent DefaultPersistenceDelegate from using 56 * overridden equals() method, resulting in a 57 * StackOverFlowError. Revert to PersistenceDelegate 58 * implementation. See 59 * http://forum.java.sun.com/thread.jspa?threadID= 60 * 477019&tstart=135 61 */ 62 protected boolean 63 mutatesTo(Object oldInstance, Object newInstance) 64 { 65 return (newInstance != null && oldInstance != null && 66 oldInstance.getClass() == newInstance.getClass()); 67 } 68 }; 69 BeanDescriptor d = info.getBeanDescriptor(); 70 d.setValue("persistenceDelegate", persistenceDelegate); 71 } catch (IntrospectionException e) { 72 System.out.println(e); 73 } 74 } 75 76 /** @serial */ 77 private final Object value; 78 79 /** 80 * Creates a scalar record with the given DTrace primitive. 81 * 82 * @param v DTrace primitive data value 83 * @throws NullPointerException if the given value is null 84 * @throws ClassCastException if the given value is not a DTrace 85 * primitive type listed as a possible return value of {@link 86 * #getValue()} 87 */ 88 public 89 ScalarRecord(Object v) 90 { 91 value = v; 92 validate(); 93 } 94 95 private void 96 validate() 97 { 98 if (value == null) { 99 throw new NullPointerException(); 100 } 101 // Short-circuit-evaluate common cases first 102 if (!((value instanceof Number) || 103 (value instanceof String) || 104 (value instanceof byte[]))) { 105 throw new ClassCastException("value is not a D primitive"); 106 } 107 } 108 109 /** 110 * Gets the traced D primitive value of this record. 111 * 112 * @return a non-null value whose type is one of the following: 113 * <ul> 114 * <li>{@link Number}</li> 115 * <li>{@link String}</li> 116 * <li>byte[]</li> 117 * </ul> 118 */ 119 public Object 120 getValue() 121 { 122 return value; 123 } 124 125 /** 126 * Compares the specified object with this record for equality. 127 * Defines equality as having the same value. 128 * 129 * @return {@code true} if and only if the specified object is also 130 * a {@code ScalarRecord} and the values returned by the {@link 131 * #getValue()} methods of both instances are equal, {@code false} 132 * otherwise. Values are compared using {@link 133 * java.lang.Object#equals(Object o) Object.equals()}, unless they 134 * are arrays of raw bytes, in which case they are compared using 135 * {@link java.util.Arrays#equals(byte[] a, byte[] a2)}. 136 */ 137 @Override 138 public boolean 139 equals(Object o) 140 { 141 if (o instanceof ScalarRecord) { 142 ScalarRecord r = (ScalarRecord)o; 143 if (value instanceof byte[]) { 144 if (r.value instanceof byte[]) { 145 byte[] a1 = (byte[])value; 146 byte[] a2 = (byte[])r.value; 147 return Arrays.equals(a1, a2); 148 } 149 return false; 150 } 151 return value.equals(r.value); 152 } 153 return false; 154 } 155 156 /** 157 * Overridden to ensure that equal instances have equal hashcodes. 158 * 159 * @return {@link java.lang.Object#hashCode()} of {@link 160 * #getValue()}, or {@link java.util.Arrays#hashCode(byte[] a)} if 161 * the value is a raw byte array 162 */ 163 @Override 164 public int 165 hashCode() 166 { 167 if (value instanceof byte[]) { 168 return Arrays.hashCode((byte[])value); 169 } 170 return value.hashCode(); 171 } 172 173 private static final int BYTE_SIGN_BIT = 1 << 7; 174 175 /** 176 * Static utility for treating a byte as unsigned by converting it 177 * to int without sign extending. 178 */ 179 static int 180 unsignedByte(byte b) 181 { 182 if (b < 0) { 183 b ^= (byte)BYTE_SIGN_BIT; 184 return ((int)b) | BYTE_SIGN_BIT; 185 } 186 return (int)b; 187 } 188 189 static String 190 hexString(int n, int width) 191 { 192 String s = Integer.toHexString(n); 193 int len = s.length(); 194 if (width < len) { 195 s = s.substring(len - width); 196 } else if (width > len) { 197 s = (spaces(width - len) + s); 198 } 199 return s; 200 } 201 202 static String 203 spaces(int n) 204 { 205 StringBuffer buf = new StringBuffer(); 206 for (int i = 0; i < n; ++i) { 207 buf.append(' '); 208 } 209 return buf.toString(); 210 } 211 212 /** 213 * Represents a byte array as a table of unsigned byte values in hex, 214 * 16 per row ending in their corresponding character 215 * representations (or a period (.) for each unprintable 216 * character). Uses default indentation. 217 * 218 * @see ScalarRecord#rawBytesString(byte[] bytes, int indent) 219 */ 220 static String 221 rawBytesString(byte[] bytes) 222 { 223 return rawBytesString(bytes, RAW_BYTES_INDENT); 224 } 225 226 /** 227 * Represents a byte array as a table of unsigned byte values in hex, 228 * 16 per row ending in their corresponding character 229 * representations (or a period (.) for each unprintable 230 * character). The table begins and ends with a newline, includes a 231 * header row, and uses a newline at the end of each row. 232 * 233 * @param bytes array of raw bytes treated as unsigned when 234 * converted to hex display 235 * @param indent number of spaces to indent each line of the 236 * returned string 237 * @return table representation of 16 bytes per row as hex and 238 * character values 239 */ 240 static String 241 rawBytesString(byte[] bytes, int indent) 242 { 243 // ported from libdtrace/common/dt_consume.c dt_print_bytes() 244 int i, j; 245 int u; 246 StringBuffer buf = new StringBuffer(); 247 String leftMargin = spaces(indent); 248 buf.append('\n'); 249 buf.append(leftMargin); 250 buf.append(" "); 251 for (i = 0; i < 16; i++) { 252 buf.append(" "); 253 buf.append("0123456789abcdef".charAt(i)); 254 } 255 buf.append(" 0123456789abcdef\n"); 256 int nbytes = bytes.length; 257 String hex; 258 for (i = 0; i < nbytes; i += 16) { 259 buf.append(leftMargin); 260 buf.append(hexString(i, 5)); 261 buf.append(':'); 262 263 for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { 264 buf.append(hexString(unsignedByte(bytes[j]), 3)); 265 } 266 267 while ((j++ % 16) != 0) { 268 buf.append(" "); 269 } 270 271 buf.append(" "); 272 273 for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { 274 u = unsignedByte(bytes[j]); 275 if ((u < ' ') || (u > '~')) { 276 buf.append('.'); 277 } else { 278 buf.append((char) u); 279 } 280 } 281 282 buf.append('\n'); 283 } 284 285 return buf.toString(); 286 } 287 288 static String 289 valueToString(Object value) 290 { 291 String s; 292 if (value instanceof byte[]) { 293 s = rawBytesString((byte[])value); 294 } else { 295 s = value.toString(); 296 } 297 return s; 298 } 299 300 private void 301 readObject(ObjectInputStream s) 302 throws IOException, ClassNotFoundException 303 { 304 s.defaultReadObject(); 305 // check class invariants 306 try { 307 validate(); 308 } catch (Exception e) { 309 throw new InvalidObjectException(e.getMessage()); 310 } 311 } 312 313 /** 314 * Gets the natural string representation of the traced D primitive. 315 * 316 * @return the value of {@link Object#toString} when called on 317 * {@link #getValue()}; or if the value is an array of raw bytes, a 318 * table displaying 16 bytes per row in unsigned hex followed by the 319 * ASCII character representations of those bytes (each unprintable 320 * character is represented by a period (.)) 321 */ 322 public String 323 toString() 324 { 325 return ScalarRecord.valueToString(getValue()); 326 } 327 } 328