1fb3fb4f3Stomee /* 2fb3fb4f3Stomee * CDDL HEADER START 3fb3fb4f3Stomee * 4fb3fb4f3Stomee * The contents of this file are subject to the terms of the 5fb3fb4f3Stomee * Common Development and Distribution License (the "License"). 6fb3fb4f3Stomee * You may not use this file except in compliance with the License. 7fb3fb4f3Stomee * 8fb3fb4f3Stomee * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fb3fb4f3Stomee * or http://www.opensolaris.org/os/licensing. 10fb3fb4f3Stomee * See the License for the specific language governing permissions 11fb3fb4f3Stomee * and limitations under the License. 12fb3fb4f3Stomee * 13fb3fb4f3Stomee * When distributing Covered Code, include this CDDL HEADER in each 14fb3fb4f3Stomee * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fb3fb4f3Stomee * If applicable, add the following below this CDDL HEADER, with the 16fb3fb4f3Stomee * fields enclosed by brackets "[]" replaced with your own identifying 17fb3fb4f3Stomee * information: Portions Copyright [yyyy] [name of copyright owner] 18fb3fb4f3Stomee * 19fb3fb4f3Stomee * CDDL HEADER END 20fb3fb4f3Stomee */ 21fb3fb4f3Stomee 22fb3fb4f3Stomee /* 2391cfa10aStomee * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24fb3fb4f3Stomee * Use is subject to license terms. 25fb3fb4f3Stomee * 26fb3fb4f3Stomee * ident "%Z%%M% %I% %E% SMI" 27fb3fb4f3Stomee */ 28fb3fb4f3Stomee package org.opensolaris.os.dtrace; 29fb3fb4f3Stomee 30fb3fb4f3Stomee import java.io.*; 31fb3fb4f3Stomee import java.util.Arrays; 32fb3fb4f3Stomee import java.beans.*; 33fb3fb4f3Stomee 34fb3fb4f3Stomee /** 35fb3fb4f3Stomee * A traced D primitive generated by a DTrace action such as {@code 36fb3fb4f3Stomee * trace()} or {@code tracemem()}, or else an element in a composite 37fb3fb4f3Stomee * value generated by DTrace. 38fb3fb4f3Stomee * <p> 39fb3fb4f3Stomee * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 40fb3fb4f3Stomee * 41fb3fb4f3Stomee * @author Tom Erickson 42fb3fb4f3Stomee */ 43fb3fb4f3Stomee public final class ScalarRecord implements ValueRecord, Serializable { 44127bbe13Stomee static final long serialVersionUID = -6920826443240176724L; 45fb3fb4f3Stomee static final int RAW_BYTES_INDENT = 5; 46fb3fb4f3Stomee 47fb3fb4f3Stomee static { 48fb3fb4f3Stomee try { 49fb3fb4f3Stomee BeanInfo info = Introspector.getBeanInfo(ScalarRecord.class); 50fb3fb4f3Stomee PersistenceDelegate persistenceDelegate = 51fb3fb4f3Stomee new DefaultPersistenceDelegate( 52127bbe13Stomee new String[] {"value", "numberOfBytes"}) 53fb3fb4f3Stomee { 54fb3fb4f3Stomee /* 55fb3fb4f3Stomee * Need to prevent DefaultPersistenceDelegate from using 56fb3fb4f3Stomee * overridden equals() method, resulting in a 57fb3fb4f3Stomee * StackOverFlowError. Revert to PersistenceDelegate 58fb3fb4f3Stomee * implementation. See 59fb3fb4f3Stomee * http://forum.java.sun.com/thread.jspa?threadID= 60fb3fb4f3Stomee * 477019&tstart=135 61fb3fb4f3Stomee */ 62fb3fb4f3Stomee protected boolean 63fb3fb4f3Stomee mutatesTo(Object oldInstance, Object newInstance) 64fb3fb4f3Stomee { 65fb3fb4f3Stomee return (newInstance != null && oldInstance != null && 66fb3fb4f3Stomee oldInstance.getClass() == newInstance.getClass()); 67fb3fb4f3Stomee } 68fb3fb4f3Stomee }; 69fb3fb4f3Stomee BeanDescriptor d = info.getBeanDescriptor(); 70fb3fb4f3Stomee d.setValue("persistenceDelegate", persistenceDelegate); 71fb3fb4f3Stomee } catch (IntrospectionException e) { 72fb3fb4f3Stomee System.out.println(e); 73fb3fb4f3Stomee } 74fb3fb4f3Stomee } 75fb3fb4f3Stomee 76fb3fb4f3Stomee /** @serial */ 77fb3fb4f3Stomee private final Object value; 78127bbe13Stomee /** @serial */ 79127bbe13Stomee private int numberOfBytes; 80fb3fb4f3Stomee 81fb3fb4f3Stomee /** 82127bbe13Stomee * Creates a scalar record with the given DTrace primitive and the 83127bbe13Stomee * number of bytes used to store the primitive in the native DTrace 84127bbe13Stomee * buffer. Since traced 8- and 16-bit integers are promoted (as 85127bbe13Stomee * unsigned values) to 32-bit integers, it may be important for 86127bbe13Stomee * output formatting to know the number of bytes used to represent 87127bbe13Stomee * the primitive before promotion. 88fb3fb4f3Stomee * 89fb3fb4f3Stomee * @param v DTrace primitive data value 90127bbe13Stomee * @param nativeByteCount number of bytes used to store the given 91127bbe13Stomee * primitive in the native DTrace buffer 92127bbe13Stomee * @throws NullPointerException if the given value is {@code null} 93127bbe13Stomee * @throws IllegalArgumentException if the given number of bytes is 94127bbe13Stomee * not consistent with the given primitive type or is not greater 95127bbe13Stomee * than zero 96fb3fb4f3Stomee * @throws ClassCastException if the given value is not a DTrace 97fb3fb4f3Stomee * primitive type listed as a possible return value of {@link 98fb3fb4f3Stomee * #getValue()} 99fb3fb4f3Stomee */ 100fb3fb4f3Stomee public ScalarRecord(Object v, int nativeByteCount)101127bbe13Stomee ScalarRecord(Object v, int nativeByteCount) 102fb3fb4f3Stomee { 103fb3fb4f3Stomee value = v; 104127bbe13Stomee numberOfBytes = nativeByteCount; 105fb3fb4f3Stomee validate(); 106fb3fb4f3Stomee } 107fb3fb4f3Stomee 10891cfa10aStomee private final void validate()109fb3fb4f3Stomee validate() 110fb3fb4f3Stomee { 111fb3fb4f3Stomee if (value == null) { 112fb3fb4f3Stomee throw new NullPointerException(); 113fb3fb4f3Stomee } 114127bbe13Stomee 115fb3fb4f3Stomee // Short-circuit-evaluate common cases first 116127bbe13Stomee if (value instanceof Integer) { 117127bbe13Stomee switch (numberOfBytes) { 118127bbe13Stomee case 1: 119127bbe13Stomee case 2: 120127bbe13Stomee case 4: 121127bbe13Stomee break; 122127bbe13Stomee default: 123127bbe13Stomee throw new IllegalArgumentException( 124127bbe13Stomee "number of bytes is " + numberOfBytes + 125127bbe13Stomee ", expected 1, 2, or 4 for Integer primitive"); 126127bbe13Stomee } 127127bbe13Stomee } else if (value instanceof Long) { 128127bbe13Stomee if (numberOfBytes != 8) { 129127bbe13Stomee throw new IllegalArgumentException( 130127bbe13Stomee "number of bytes is " + numberOfBytes + 131127bbe13Stomee ", expected 8 for Long primitive"); 132127bbe13Stomee } 133127bbe13Stomee } else if ((value instanceof String) || (value instanceof byte[])) { 134127bbe13Stomee switch (numberOfBytes) { 135127bbe13Stomee case 1: 136127bbe13Stomee case 2: 137127bbe13Stomee case 4: 138127bbe13Stomee case 8: 139127bbe13Stomee throw new IllegalArgumentException( 140127bbe13Stomee "number of bytes is " + numberOfBytes + 141127bbe13Stomee ", expected a number other than " + 142127bbe13Stomee "1, 2, 4, or 8 for String or byte-array " + 143127bbe13Stomee "primitive"); 144127bbe13Stomee } 145127bbe13Stomee } else if (value instanceof Number) { 146127bbe13Stomee if (numberOfBytes <= 0) { 147127bbe13Stomee throw new IllegalArgumentException( 148127bbe13Stomee "number of bytes is " + numberOfBytes + 149127bbe13Stomee ", must be greater than zero"); 150127bbe13Stomee } 151127bbe13Stomee } else { 152127bbe13Stomee throw new ClassCastException(value.getClass().getName() + 153127bbe13Stomee " value is not a D primitive"); 154127bbe13Stomee } 155fb3fb4f3Stomee } 156fb3fb4f3Stomee 157fb3fb4f3Stomee /** 158fb3fb4f3Stomee * Gets the traced D primitive value of this record. 159fb3fb4f3Stomee * 160fb3fb4f3Stomee * @return a non-null value whose type is one of the following: 161fb3fb4f3Stomee * <ul> 162fb3fb4f3Stomee * <li>{@link Number}</li> 163fb3fb4f3Stomee * <li>{@link String}</li> 164fb3fb4f3Stomee * <li>byte[]</li> 165fb3fb4f3Stomee * </ul> 166fb3fb4f3Stomee */ 167fb3fb4f3Stomee public Object getValue()168fb3fb4f3Stomee getValue() 169fb3fb4f3Stomee { 170fb3fb4f3Stomee return value; 171fb3fb4f3Stomee } 172fb3fb4f3Stomee 173127bbe13Stomee /** 174127bbe13Stomee * Gets the number of bytes used to store the primitive value of 175127bbe13Stomee * this record in the native DTrace buffer. Since traced 8- and 176127bbe13Stomee * 16-bit integers are promoted (as unsigned values) to 32-bit 177127bbe13Stomee * integers, it may be important for output formatting to know the 178127bbe13Stomee * number of bytes used to represent the primitive before promotion. 179127bbe13Stomee * 180127bbe13Stomee * @return the number of bytes used to store the primitive value 181127bbe13Stomee * of this record in the native DTrace buffer, guaranteed to be 182127bbe13Stomee * greater than zero and consisitent with the type of the primitive 183127bbe13Stomee * value 184127bbe13Stomee */ 185127bbe13Stomee public int getNumberOfBytes()186127bbe13Stomee getNumberOfBytes() 187127bbe13Stomee { 188127bbe13Stomee return numberOfBytes; 189127bbe13Stomee } 190127bbe13Stomee 191fb3fb4f3Stomee /** 192fb3fb4f3Stomee * Compares the specified object with this record for equality. 193fb3fb4f3Stomee * Defines equality as having the same value. 194fb3fb4f3Stomee * 195fb3fb4f3Stomee * @return {@code true} if and only if the specified object is also 196fb3fb4f3Stomee * a {@code ScalarRecord} and the values returned by the {@link 197fb3fb4f3Stomee * #getValue()} methods of both instances are equal, {@code false} 198fb3fb4f3Stomee * otherwise. Values are compared using {@link 199fb3fb4f3Stomee * java.lang.Object#equals(Object o) Object.equals()}, unless they 200fb3fb4f3Stomee * are arrays of raw bytes, in which case they are compared using 201fb3fb4f3Stomee * {@link java.util.Arrays#equals(byte[] a, byte[] a2)}. 202fb3fb4f3Stomee */ 203fb3fb4f3Stomee @Override 204fb3fb4f3Stomee public boolean equals(Object o)205fb3fb4f3Stomee equals(Object o) 206fb3fb4f3Stomee { 207fb3fb4f3Stomee if (o instanceof ScalarRecord) { 208fb3fb4f3Stomee ScalarRecord r = (ScalarRecord)o; 209fb3fb4f3Stomee if (value instanceof byte[]) { 210fb3fb4f3Stomee if (r.value instanceof byte[]) { 211fb3fb4f3Stomee byte[] a1 = (byte[])value; 212fb3fb4f3Stomee byte[] a2 = (byte[])r.value; 213fb3fb4f3Stomee return Arrays.equals(a1, a2); 214fb3fb4f3Stomee } 215fb3fb4f3Stomee return false; 216fb3fb4f3Stomee } 217fb3fb4f3Stomee return value.equals(r.value); 218fb3fb4f3Stomee } 219fb3fb4f3Stomee return false; 220fb3fb4f3Stomee } 221fb3fb4f3Stomee 222fb3fb4f3Stomee /** 223fb3fb4f3Stomee * Overridden to ensure that equal instances have equal hashcodes. 224fb3fb4f3Stomee * 225fb3fb4f3Stomee * @return {@link java.lang.Object#hashCode()} of {@link 226fb3fb4f3Stomee * #getValue()}, or {@link java.util.Arrays#hashCode(byte[] a)} if 227fb3fb4f3Stomee * the value is a raw byte array 228fb3fb4f3Stomee */ 229fb3fb4f3Stomee @Override 230fb3fb4f3Stomee public int hashCode()231fb3fb4f3Stomee hashCode() 232fb3fb4f3Stomee { 233fb3fb4f3Stomee if (value instanceof byte[]) { 234fb3fb4f3Stomee return Arrays.hashCode((byte[])value); 235fb3fb4f3Stomee } 236fb3fb4f3Stomee return value.hashCode(); 237fb3fb4f3Stomee } 238fb3fb4f3Stomee 239fb3fb4f3Stomee private static final int BYTE_SIGN_BIT = 1 << 7; 240fb3fb4f3Stomee 241fb3fb4f3Stomee /** 242fb3fb4f3Stomee * Static utility for treating a byte as unsigned by converting it 243fb3fb4f3Stomee * to int without sign extending. 244fb3fb4f3Stomee */ 245fb3fb4f3Stomee static int unsignedByte(byte b)246fb3fb4f3Stomee unsignedByte(byte b) 247fb3fb4f3Stomee { 248fb3fb4f3Stomee if (b < 0) { 249fb3fb4f3Stomee b ^= (byte)BYTE_SIGN_BIT; 250fb3fb4f3Stomee return ((int)b) | BYTE_SIGN_BIT; 251fb3fb4f3Stomee } 252fb3fb4f3Stomee return (int)b; 253fb3fb4f3Stomee } 254fb3fb4f3Stomee 255fb3fb4f3Stomee static String hexString(int n, int width)256fb3fb4f3Stomee hexString(int n, int width) 257fb3fb4f3Stomee { 258fb3fb4f3Stomee String s = Integer.toHexString(n); 259fb3fb4f3Stomee int len = s.length(); 260fb3fb4f3Stomee if (width < len) { 261fb3fb4f3Stomee s = s.substring(len - width); 262fb3fb4f3Stomee } else if (width > len) { 263fb3fb4f3Stomee s = (spaces(width - len) + s); 264fb3fb4f3Stomee } 265fb3fb4f3Stomee return s; 266fb3fb4f3Stomee } 267fb3fb4f3Stomee 268fb3fb4f3Stomee static String spaces(int n)269fb3fb4f3Stomee spaces(int n) 270fb3fb4f3Stomee { 271*4ae67516Stomee StringBuilder buf = new StringBuilder(); 272fb3fb4f3Stomee for (int i = 0; i < n; ++i) { 273fb3fb4f3Stomee buf.append(' '); 274fb3fb4f3Stomee } 275fb3fb4f3Stomee return buf.toString(); 276fb3fb4f3Stomee } 277fb3fb4f3Stomee 278fb3fb4f3Stomee /** 279fb3fb4f3Stomee * Represents a byte array as a table of unsigned byte values in hex, 280fb3fb4f3Stomee * 16 per row ending in their corresponding character 281fb3fb4f3Stomee * representations (or a period (.) for each unprintable 282fb3fb4f3Stomee * character). Uses default indentation. 283fb3fb4f3Stomee * 284fb3fb4f3Stomee * @see ScalarRecord#rawBytesString(byte[] bytes, int indent) 285fb3fb4f3Stomee */ 286fb3fb4f3Stomee static String rawBytesString(byte[] bytes)287fb3fb4f3Stomee rawBytesString(byte[] bytes) 288fb3fb4f3Stomee { 289fb3fb4f3Stomee return rawBytesString(bytes, RAW_BYTES_INDENT); 290fb3fb4f3Stomee } 291fb3fb4f3Stomee 292fb3fb4f3Stomee /** 293fb3fb4f3Stomee * Represents a byte array as a table of unsigned byte values in hex, 294fb3fb4f3Stomee * 16 per row ending in their corresponding character 295fb3fb4f3Stomee * representations (or a period (.) for each unprintable 296fb3fb4f3Stomee * character). The table begins and ends with a newline, includes a 297fb3fb4f3Stomee * header row, and uses a newline at the end of each row. 298fb3fb4f3Stomee * 299fb3fb4f3Stomee * @param bytes array of raw bytes treated as unsigned when 300fb3fb4f3Stomee * converted to hex display 301fb3fb4f3Stomee * @param indent number of spaces to indent each line of the 302fb3fb4f3Stomee * returned string 303fb3fb4f3Stomee * @return table representation of 16 bytes per row as hex and 304fb3fb4f3Stomee * character values 305fb3fb4f3Stomee */ 306fb3fb4f3Stomee static String rawBytesString(byte[] bytes, int indent)307fb3fb4f3Stomee rawBytesString(byte[] bytes, int indent) 308fb3fb4f3Stomee { 309fb3fb4f3Stomee // ported from libdtrace/common/dt_consume.c dt_print_bytes() 310fb3fb4f3Stomee int i, j; 311fb3fb4f3Stomee int u; 312*4ae67516Stomee StringBuilder buf = new StringBuilder(); 313fb3fb4f3Stomee String leftMargin = spaces(indent); 314fb3fb4f3Stomee buf.append('\n'); 315fb3fb4f3Stomee buf.append(leftMargin); 316fb3fb4f3Stomee buf.append(" "); 317fb3fb4f3Stomee for (i = 0; i < 16; i++) { 318fb3fb4f3Stomee buf.append(" "); 319fb3fb4f3Stomee buf.append("0123456789abcdef".charAt(i)); 320fb3fb4f3Stomee } 321fb3fb4f3Stomee buf.append(" 0123456789abcdef\n"); 322fb3fb4f3Stomee int nbytes = bytes.length; 323fb3fb4f3Stomee String hex; 324fb3fb4f3Stomee for (i = 0; i < nbytes; i += 16) { 325fb3fb4f3Stomee buf.append(leftMargin); 326fb3fb4f3Stomee buf.append(hexString(i, 5)); 327fb3fb4f3Stomee buf.append(':'); 328fb3fb4f3Stomee 329fb3fb4f3Stomee for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { 330fb3fb4f3Stomee buf.append(hexString(unsignedByte(bytes[j]), 3)); 331fb3fb4f3Stomee } 332fb3fb4f3Stomee 333fb3fb4f3Stomee while ((j++ % 16) != 0) { 334fb3fb4f3Stomee buf.append(" "); 335fb3fb4f3Stomee } 336fb3fb4f3Stomee 337fb3fb4f3Stomee buf.append(" "); 338fb3fb4f3Stomee 339fb3fb4f3Stomee for (j = i; (j < (i + 16)) && (j < nbytes); ++j) { 340fb3fb4f3Stomee u = unsignedByte(bytes[j]); 341fb3fb4f3Stomee if ((u < ' ') || (u > '~')) { 342fb3fb4f3Stomee buf.append('.'); 343fb3fb4f3Stomee } else { 344fb3fb4f3Stomee buf.append((char) u); 345fb3fb4f3Stomee } 346fb3fb4f3Stomee } 347fb3fb4f3Stomee 348fb3fb4f3Stomee buf.append('\n'); 349fb3fb4f3Stomee } 350fb3fb4f3Stomee 351fb3fb4f3Stomee return buf.toString(); 352fb3fb4f3Stomee } 353fb3fb4f3Stomee 354fb3fb4f3Stomee static String valueToString(Object value)355fb3fb4f3Stomee valueToString(Object value) 356fb3fb4f3Stomee { 357fb3fb4f3Stomee String s; 358fb3fb4f3Stomee if (value instanceof byte[]) { 359fb3fb4f3Stomee s = rawBytesString((byte[])value); 360fb3fb4f3Stomee } else { 361fb3fb4f3Stomee s = value.toString(); 362fb3fb4f3Stomee } 363fb3fb4f3Stomee return s; 364fb3fb4f3Stomee } 365fb3fb4f3Stomee 366fb3fb4f3Stomee private void readObject(ObjectInputStream s)367fb3fb4f3Stomee readObject(ObjectInputStream s) 368fb3fb4f3Stomee throws IOException, ClassNotFoundException 369fb3fb4f3Stomee { 370fb3fb4f3Stomee s.defaultReadObject(); 371fb3fb4f3Stomee // check class invariants 372fb3fb4f3Stomee try { 373fb3fb4f3Stomee validate(); 374fb3fb4f3Stomee } catch (Exception e) { 375*4ae67516Stomee InvalidObjectException x = new InvalidObjectException( 376*4ae67516Stomee e.getMessage()); 377*4ae67516Stomee x.initCause(e); 378*4ae67516Stomee throw x; 379fb3fb4f3Stomee } 380fb3fb4f3Stomee } 381fb3fb4f3Stomee 382fb3fb4f3Stomee /** 383fb3fb4f3Stomee * Gets the natural string representation of the traced D primitive. 384fb3fb4f3Stomee * 385fb3fb4f3Stomee * @return the value of {@link Object#toString} when called on 386fb3fb4f3Stomee * {@link #getValue()}; or if the value is an array of raw bytes, a 387fb3fb4f3Stomee * table displaying 16 bytes per row in unsigned hex followed by the 388fb3fb4f3Stomee * ASCII character representations of those bytes (each unprintable 389fb3fb4f3Stomee * character is represented by a period (.)) 390fb3fb4f3Stomee */ 391fb3fb4f3Stomee public String toString()392fb3fb4f3Stomee toString() 393fb3fb4f3Stomee { 394fb3fb4f3Stomee return ScalarRecord.valueToString(getValue()); 395fb3fb4f3Stomee } 396fb3fb4f3Stomee } 397