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