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.beans.*; 32 33 /** 34 * A value generated by the DTrace {@code umod()}, {@code ufunc()}, or 35 * {@code usym()} action used to lookup the symbol associated with a 36 * user address. 37 * <p> 38 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 39 * 40 * @author Tom Erickson 41 */ 42 public final class UserSymbolRecord implements SymbolValueRecord, 43 Serializable, Comparable <UserSymbolRecord> 44 { 45 static final long serialVersionUID = -591954442654439794L; 46 47 static { 48 try { 49 BeanInfo info = Introspector.getBeanInfo(UserSymbolRecord.class); 50 PersistenceDelegate persistenceDelegate = 51 new DefaultPersistenceDelegate( 52 new String[] {"processID", "symbol", "address"}) 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 e.printStackTrace(); 73 } 74 } 75 76 // serialized explicitly to hide implementation; treat as final 77 private transient Value value; 78 // set natively after creation; treat as final 79 private transient String symbol; 80 81 /** 82 * Called by native code. 83 */ 84 private 85 UserSymbolRecord(int pid, long addressValue) 86 { 87 value = new Value(pid, addressValue); 88 } 89 90 /** 91 * Creates a {@code UserSymbolRecord} with the given process ID, 92 * symbol lookup, and user address converted in probe context as a 93 * result of the DTrace {@code umod()}, {@code ufunc()}, or {@code 94 * usym()} action. 95 * <p> 96 * Supports XML persistence. 97 * 98 * @param pid non-negative user process ID 99 * @param lookupValue the result in the native DTrace library of 100 * looking up the symbol associated with the given user address 101 * @param addressValue symbol address 102 * @throws NullPointerException if the given lookup value is {@code null} 103 * @throws IllegalArgumentException if the given process ID is 104 * negative 105 */ 106 public 107 UserSymbolRecord(int pid, String lookupValue, long addressValue) 108 { 109 value = new Value(pid, addressValue); 110 symbol = lookupValue; 111 validate(); 112 } 113 114 private void 115 validate() 116 { 117 if (symbol == null) { 118 throw new NullPointerException("symbol is null"); 119 } 120 } 121 122 /** 123 * Gets the process ID associated with this record's symbol. 124 * 125 * @return non-negative pid 126 */ 127 public int 128 getProcessID() 129 { 130 return value.getProcessID(); 131 } 132 133 /** 134 * Gets the result of the address lookup in the same form returned 135 * by {@link Consumer#lookupUserFunction(int pid, long address)}. 136 * 137 * @return non-null address lookup in the format defined by the 138 * native DTrace library 139 */ 140 public String 141 getSymbol() 142 { 143 return symbol; 144 } 145 146 /** 147 * Called by native code and ProbeData addSymbolRecord() 148 */ 149 void 150 setSymbol(String lookupValue) 151 { 152 symbol = lookupValue; 153 validate(); 154 } 155 156 /** 157 * Gets the symbol's user address. 158 * 159 * @return the symbol's user address 160 */ 161 public long 162 getAddress() 163 { 164 return value.getAddress(); 165 } 166 167 /** 168 * Gets the composite value of the symbol's process ID and address. 169 * The value is used in {@link #equals(Object o) equals()} and 170 * {@link #compareTo(UserSymbolRecord r) compareTo()} to test 171 * equality and to determine the natural ordering of {@code 172 * UserSymbolRecord} instances. 173 * 174 * @return non-null composite value of the symbols's process ID and 175 * address 176 */ 177 public Value 178 getValue() 179 { 180 return value; 181 } 182 183 /** 184 * Compares the specified object with this {@code UserSymbolRecord} 185 * for equality. Returns {@code true} if and only if the specified 186 * object is also a {@code UserSymbolRecord} and both records have 187 * the same process ID and the same address. 188 * 189 * @return {@code true} if and only if the specified object is also 190 * a {@code UserSymbolRecord} and both records have the same 191 * process ID and the same address 192 */ 193 @Override 194 public boolean 195 equals(Object o) 196 { 197 if (o instanceof UserSymbolRecord) { 198 UserSymbolRecord r = (UserSymbolRecord)o; 199 return value.equals(r.value); 200 } 201 return false; 202 } 203 204 /** 205 * Overridden to ensure that equal instances have equal hash codes. 206 */ 207 @Override 208 public int 209 hashCode() 210 { 211 return value.hashCode(); 212 } 213 214 /** 215 * Compares this record with the given user symbol lookup and orders 216 * by the combined value of process ID first and address second. 217 * The comparison treats addresses as unsigned values so the 218 * ordering is consistent with that defined in the native DTrace 219 * library. The {@code compareTo()} method is compatible with {@link 220 * #equals(Object o) equals()}. 221 * 222 * @return -1, 0, or 1 as this record's combined process ID and 223 * address is less than, equal to, or greater than the given 224 * record's combined process ID and address 225 */ 226 public int 227 compareTo(UserSymbolRecord r) 228 { 229 return value.compareTo(r.value); 230 } 231 232 /** 233 * Serialize this {@code UserSymbolRecord} instance. 234 * 235 * @serialData processID ({@code int}), followed by symbol ({@code 236 * java.lang.String}), followed by address ({@code long}) 237 */ 238 private void 239 writeObject(ObjectOutputStream s) throws IOException 240 { 241 s.defaultWriteObject(); 242 s.writeInt(getProcessID()); 243 s.writeObject(getSymbol()); 244 s.writeLong(getAddress()); 245 } 246 247 private void 248 readObject(ObjectInputStream s) 249 throws IOException, ClassNotFoundException 250 { 251 s.defaultReadObject(); 252 int pid = s.readInt(); 253 symbol = (String)s.readObject(); 254 long addressValue = s.readLong(); 255 try { 256 value = new Value(pid, addressValue); 257 validate(); 258 } catch (Exception e) { 259 throw new InvalidObjectException(e.getMessage()); 260 } 261 } 262 263 /** 264 * Gets the result of this symbol lookup. The format is defined in 265 * the native DTrace library and is as stable as that library 266 * definition. 267 * 268 * @return {@link #getSymbol()} 269 */ 270 @Override 271 public String 272 toString() 273 { 274 return symbol; 275 } 276 277 /** 278 * The composite value of a symbol's process ID and user address. 279 * <p> 280 * Immutable. Supports persistence using {@link 281 * java.beans.XMLEncoder}. 282 */ 283 public static final class Value implements Serializable, 284 Comparable <Value> { 285 static final long serialVersionUID = -91449037747641135L; 286 287 static { 288 try { 289 BeanInfo info = Introspector.getBeanInfo( 290 UserSymbolRecord.Value.class); 291 PersistenceDelegate persistenceDelegate = 292 new DefaultPersistenceDelegate( 293 new String[] {"processID", "address"}) 294 { 295 /* 296 * Need to prevent DefaultPersistenceDelegate from using 297 * overridden equals() method, resulting in a 298 * StackOverFlowError. Revert to PersistenceDelegate 299 * implementation. See 300 * http://forum.java.sun.com/thread.jspa?threadID= 301 * 477019&tstart=135 302 */ 303 protected boolean 304 mutatesTo(Object oldInstance, Object newInstance) 305 { 306 return (newInstance != null && oldInstance != null && 307 oldInstance.getClass() == 308 newInstance.getClass()); 309 } 310 }; 311 BeanDescriptor d = info.getBeanDescriptor(); 312 d.setValue("persistenceDelegate", persistenceDelegate); 313 } catch (IntrospectionException e) { 314 e.printStackTrace(); 315 } 316 } 317 318 /** @serial */ 319 private final int processID; 320 /** @serial */ 321 private final long address; 322 323 /** 324 * Creates a composite value with the given user process ID and 325 * symbol address. 326 * <p> 327 * Supports XML persistence. 328 * 329 * @param pid non-negative process ID 330 * @param addressValue symbol address 331 * @throws IllegalArgumentException if the given process ID is 332 * negative 333 */ 334 public 335 Value(int pid, long addressValue) 336 { 337 processID = pid; 338 address = addressValue; 339 validate(); 340 } 341 342 private void 343 validate() 344 { 345 if (processID < 0) { 346 throw new IllegalArgumentException("process ID is negative"); 347 } 348 } 349 350 /** 351 * Gets the process ID associated with this value's user 352 * address. 353 * 354 * @return non-negative pid 355 */ 356 public int 357 getProcessID() 358 { 359 return processID; 360 } 361 362 /** 363 * Gets the symbol's user address. 364 * 365 * @return the symbol's user address 366 */ 367 public long 368 getAddress() 369 { 370 return address; 371 } 372 373 /** 374 * Compares the specified object with this {@code 375 * UserSymbolRecord.Value} for equality. Returns {@code true} 376 * if and only if the specified object is also a {@code 377 * UserSymbolRecord.Value} and both values have the same process 378 * ID and the same address. 379 * 380 * @return {@code true} if and only if the specified object is 381 * also a {@code UserSymbolRecord.Value} and both values have 382 * the same process ID and the same address 383 */ 384 @Override 385 public boolean 386 equals(Object o) 387 { 388 if (o instanceof Value) { 389 Value v = (Value)o; 390 return ((processID == v.processID) && (address == v.address)); 391 } 392 return false; 393 } 394 395 /** 396 * Overridden to ensure that equal instances have equal hash 397 * codes. 398 */ 399 @Override 400 public int 401 hashCode() 402 { 403 int hash = 17; 404 hash = 37 * hash + processID; 405 hash = 37 * hash + (int)(address ^ (address >>> 32)); 406 return hash; 407 } 408 409 /** 410 * Compares this value with the given {@code 411 * UserSymbolRecord.Value} and orders by process ID first and 412 * address second. The comparison treats addresses as unsigned 413 * values so the ordering is consistent with that defined in the 414 * native DTrace library. The {@code compareTo()} method is 415 * compatible with {@link #equals(Object o) equals()}. 416 * 417 * @return -1, 0, or 1 as this value's combined process ID and 418 * address is less than, equal to, or greater than the given 419 * value's combined process ID and address 420 */ 421 public int 422 compareTo(Value v) 423 { 424 int cmp; 425 cmp = (processID < v.processID ? -1 : 426 (processID > v.processID ? 1 : 0)); 427 if (cmp == 0) { 428 cmp = ProbeData.compareUnsigned(address, v.address); 429 } 430 return cmp; 431 } 432 433 private void 434 readObject(ObjectInputStream s) 435 throws IOException, ClassNotFoundException 436 { 437 s.defaultReadObject(); 438 // check class invariants 439 try { 440 validate(); 441 } catch (Exception e) { 442 throw new InvalidObjectException(e.getMessage()); 443 } 444 } 445 446 /** 447 * Gets a string representation of this {@code 448 * UserSymbolRecord.Value} instance useful for logging and not 449 * intended for display. The exact details of the 450 * representation are unspecified and subject to change, but the 451 * following format may be regarded as typical: 452 * <pre><code> 453 * class-name[property1 = value1, property2 = value2] 454 * </code></pre> 455 */ 456 public String 457 toString() 458 { 459 StringBuilder buf = new StringBuilder(); 460 buf.append(Value.class.getName()); 461 buf.append("[processID = "); 462 buf.append(processID); 463 buf.append(", address = "); 464 buf.append(address); 465 buf.append(']'); 466 return buf.toString(); 467 } 468 } 469 } 470