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.util.*; 33 34 /** 35 * A formatted string generated by the DTrace {@code printf()} action. 36 * <p> 37 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 38 * 39 * @author Tom Erickson 40 */ 41 public final class PrintfRecord implements Record, Serializable, 42 Comparable <PrintfRecord> { 43 static final long serialVersionUID = 727237355963977675L; 44 45 static { 46 try { 47 BeanInfo info = Introspector.getBeanInfo(PrintfRecord.class); 48 PersistenceDelegate persistenceDelegate = 49 new DefaultPersistenceDelegate( 50 new String[] {"records", "formattedString"}) 51 { 52 /* 53 * Need to prevent DefaultPersistenceDelegate from using 54 * overridden equals() method, resulting in a 55 * StackOverFlowError. Revert to PersistenceDelegate 56 * implementation. See 57 * http://forum.java.sun.com/thread.jspa?threadID= 58 * 477019&tstart=135 59 */ 60 protected boolean 61 mutatesTo(Object oldInstance, Object newInstance) 62 { 63 return (newInstance != null && oldInstance != null && 64 oldInstance.getClass() == newInstance.getClass()); 65 } 66 }; 67 BeanDescriptor d = info.getBeanDescriptor(); 68 d.setValue("persistenceDelegate", persistenceDelegate); 69 } catch (IntrospectionException e) { 70 System.out.println(e); 71 } 72 } 73 74 /** @serial */ 75 private List <ValueRecord> records; 76 /** @serial */ 77 private String formattedString; 78 79 // package-level access, called by ProbeData PrintfRecord()80 PrintfRecord() 81 { 82 records = new ArrayList <ValueRecord> (); 83 } 84 85 /** 86 * Creates a record with the unformatted elements passed to the 87 * DTrace {@code printf()} action and the resulting formatted 88 * output. Supports XML persistence. 89 * 90 * @param v variable number of unformatted elements passed to the 91 * DTrace {@code printf()} action 92 * @param s formatted {@code printf()} output 93 * @throws NullPointerException if the given list or any of its 94 * elements is {@code null}, or if the given formatted string is 95 * {@code null} 96 */ 97 public PrintfRecord(List <ValueRecord> v, String s)98 PrintfRecord(List <ValueRecord> v, String s) 99 { 100 formattedString = s; 101 records = new ArrayList <ValueRecord> (v.size()); 102 records.addAll(v); 103 validate(); 104 } 105 106 private final void validate()107 validate() 108 { 109 if (formattedString == null) { 110 throw new NullPointerException("formatted string is null"); 111 } 112 if (records == null) { 113 throw new NullPointerException("list of format args is null"); 114 } 115 for (ValueRecord r : records) { 116 if (r == null) { 117 throw new NullPointerException("format arg is null"); 118 } 119 } 120 } 121 122 /** 123 * Called by ProbeData code to populate record list. 124 * 125 * @throws NullPointerException if o is null 126 */ 127 void addUnformattedElement(ScalarRecord rec)128 addUnformattedElement(ScalarRecord rec) 129 { 130 records.add(rec); 131 } 132 133 /** 134 * Gets the formatted string output of the DTrace {@code printf()} 135 * action. 136 * 137 * @return non-null formatted string output of the DTrace {@code 138 * printf()} action 139 */ 140 public String getFormattedString()141 getFormattedString() 142 { 143 return formattedString; 144 } 145 146 /** 147 * Package level access; called by ProbeData 148 */ 149 void setFormattedString(String s)150 setFormattedString(String s) 151 { 152 if (s == null) { 153 throw new NullPointerException("formatted string is null"); 154 } 155 formattedString = s; 156 } 157 158 /** 159 * Gets the unfomatted elements passed to the DTrace {@code 160 * printf()} action after the format string. 161 * 162 * @return non-null, unmodifiable list of unformatted elements 163 * passed to the DTrace {@code printf()} action that generated this 164 * record, in the order they appear in the argument list after the 165 * format string 166 */ 167 public List <ValueRecord> getRecords()168 getRecords() 169 { 170 return Collections. <ValueRecord> unmodifiableList(records); 171 } 172 173 /** 174 * Gets the number of DTrace {@code printf()} unformatted elements 175 * (arguments following the format string). For example, the 176 * following action 177 * <pre><code> 178 * printf("%s %d\n", "cat", 9); 179 * </code></pre> 180 * generates a {@code PrintfRecord} with a record count of two. 181 * 182 * @return the number of unformatted elements passed to the DTrace 183 * {@code printf()} action that generated this record. 184 */ 185 public int getRecordCount()186 getRecordCount() 187 { 188 return records.size(); 189 } 190 191 /** 192 * Gets the unformatted element passed to the DTrace {@code 193 * printf()} action at the given offset in the {@code printf()} 194 * argument list after the format string, starting at offset zero 195 * for the first unformatted element. 196 * 197 * @return non-null record representing the unformatted {@code 198 * printf()} element at the given index (using the same order that 199 * they appear in the {@code printf()} argument list) 200 * @throws ArrayIndexOutOfBoundsException if the given index is 201 * out of range (index < 0 || index >= getRecordCount()) 202 */ 203 public ValueRecord getRecord(int i)204 getRecord(int i) 205 { 206 return records.get(i); 207 } 208 209 /** 210 * Compares the specified object with this {@code PrintfRecord} for 211 * equality. Returns {@code true} if and only if the specified 212 * object is also a {@code PrintfRecord} and both records have the 213 * same formatted string and underlying data elements. 214 * 215 * @return {@code true} if and only if the specified object is also 216 * a {@code PrintfRecord} and both the formatted strings <i>and</i> 217 * the underlying data elements of both records are equal 218 */ 219 @Override 220 public boolean equals(Object o)221 equals(Object o) 222 { 223 if (o instanceof PrintfRecord) { 224 PrintfRecord r = (PrintfRecord)o; 225 return (records.equals(r.records) && 226 formattedString.equals(r.formattedString)); 227 } 228 return false; 229 } 230 231 /** 232 * Overridden to ensure that equal instances have equal hash codes. 233 */ 234 @Override 235 public int hashCode()236 hashCode() 237 { 238 int hash = 17; 239 hash = (37 * hash) + records.hashCode(); 240 hash = (37 * hash) + formattedString.hashCode(); 241 return hash; 242 } 243 244 /** 245 * Compares the formatted string value of this record with that of 246 * the given record. Note that ordering {@code printf} records by 247 * their string values is incompatible with {@link #equals(Object o) 248 * equals()}, which also checks the underlying data elements for 249 * equality. 250 * 251 * @return a negative number, 0, or a positive number as this 252 * record's formatted string is lexicographically less than, equal 253 * to, or greater than the given record's formatted string 254 */ 255 public int compareTo(PrintfRecord r)256 compareTo(PrintfRecord r) 257 { 258 return formattedString.compareTo(r.formattedString); 259 } 260 261 private void readObject(ObjectInputStream s)262 readObject(ObjectInputStream s) 263 throws IOException, ClassNotFoundException 264 { 265 s.defaultReadObject(); 266 // Defensively copy record list before validating 267 if (records == null) { 268 throw new InvalidObjectException("record list is null"); 269 } 270 List <ValueRecord> copy = new ArrayList <ValueRecord> (records.size()); 271 copy.addAll(records); 272 records = copy; 273 // check invariants 274 try { 275 validate(); 276 } catch (Exception e) { 277 InvalidObjectException x = new InvalidObjectException( 278 e.getMessage()); 279 x.initCause(e); 280 throw x; 281 } 282 } 283 284 /** 285 * Gets the formatted string output of the DTrace {@code printf()} 286 * action. 287 */ 288 public String toString()289 toString() 290 { 291 return formattedString; 292 } 293 } 294