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 2007 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 UserSymbolRecord(int pid, long addressValue)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 UserSymbolRecord(int pid, String lookupValue, long addressValue)107 UserSymbolRecord(int pid, String lookupValue, long addressValue) 108 { 109 value = new Value(pid, addressValue); 110 symbol = lookupValue; 111 validate(); 112 } 113 114 private final void validate()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 getProcessID()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 getSymbol()141 getSymbol() 142 { 143 return symbol; 144 } 145 146 /** 147 * Called by native code and ProbeData addSymbolRecord() 148 */ 149 void setSymbol(String lookupValue)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 getAddress()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 getValue()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 equals(Object o)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 hashCode()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 compareTo(UserSymbolRecord r)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 writeObject(ObjectOutputStream s)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 readObject(ObjectInputStream s)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 InvalidObjectException x = new InvalidObjectException( 260 e.getMessage()); 261 x.initCause(e); 262 throw x; 263 } 264 } 265 266 /** 267 * Gets the result of this symbol lookup. The format is defined in 268 * the native DTrace library and is as stable as that library 269 * definition. 270 * 271 * @return {@link #getSymbol()} 272 */ 273 @Override 274 public String toString()275 toString() 276 { 277 return symbol; 278 } 279 280 /** 281 * The composite value of a symbol's process ID and user address. 282 * <p> 283 * Immutable. Supports persistence using {@link 284 * java.beans.XMLEncoder}. 285 */ 286 public static final class Value implements Serializable, 287 Comparable <Value> { 288 static final long serialVersionUID = -91449037747641135L; 289 290 static { 291 try { 292 BeanInfo info = Introspector.getBeanInfo( 293 UserSymbolRecord.Value.class); 294 PersistenceDelegate persistenceDelegate = 295 new DefaultPersistenceDelegate( 296 new String[] {"processID", "address"}) 297 { 298 /* 299 * Need to prevent DefaultPersistenceDelegate from using 300 * overridden equals() method, resulting in a 301 * StackOverFlowError. Revert to PersistenceDelegate 302 * implementation. See 303 * http://forum.java.sun.com/thread.jspa?threadID= 304 * 477019&tstart=135 305 */ 306 protected boolean 307 mutatesTo(Object oldInstance, Object newInstance) 308 { 309 return (newInstance != null && oldInstance != null && 310 oldInstance.getClass() == 311 newInstance.getClass()); 312 } 313 }; 314 BeanDescriptor d = info.getBeanDescriptor(); 315 d.setValue("persistenceDelegate", persistenceDelegate); 316 } catch (IntrospectionException e) { 317 e.printStackTrace(); 318 } 319 } 320 321 /** @serial */ 322 private final int processID; 323 /** @serial */ 324 private final long address; 325 326 /** 327 * Creates a composite value with the given user process ID and 328 * symbol address. 329 * <p> 330 * Supports XML persistence. 331 * 332 * @param pid non-negative process ID 333 * @param addressValue symbol address 334 * @throws IllegalArgumentException if the given process ID is 335 * negative 336 */ 337 public Value(int pid, long addressValue)338 Value(int pid, long addressValue) 339 { 340 processID = pid; 341 address = addressValue; 342 validate(); 343 } 344 345 private final void validate()346 validate() 347 { 348 if (processID < 0) { 349 throw new IllegalArgumentException("process ID is negative"); 350 } 351 } 352 353 /** 354 * Gets the process ID associated with this value's user 355 * address. 356 * 357 * @return non-negative pid 358 */ 359 public int getProcessID()360 getProcessID() 361 { 362 return processID; 363 } 364 365 /** 366 * Gets the symbol's user address. 367 * 368 * @return the symbol's user address 369 */ 370 public long getAddress()371 getAddress() 372 { 373 return address; 374 } 375 376 /** 377 * Compares the specified object with this {@code 378 * UserSymbolRecord.Value} for equality. Returns {@code true} 379 * if and only if the specified object is also a {@code 380 * UserSymbolRecord.Value} and both values have the same process 381 * ID and the same address. 382 * 383 * @return {@code true} if and only if the specified object is 384 * also a {@code UserSymbolRecord.Value} and both values have 385 * the same process ID and the same address 386 */ 387 @Override 388 public boolean equals(Object o)389 equals(Object o) 390 { 391 if (o instanceof Value) { 392 Value v = (Value)o; 393 return ((processID == v.processID) && (address == v.address)); 394 } 395 return false; 396 } 397 398 /** 399 * Overridden to ensure that equal instances have equal hash 400 * codes. 401 */ 402 @Override 403 public int hashCode()404 hashCode() 405 { 406 int hash = 17; 407 hash = 37 * hash + processID; 408 hash = 37 * hash + (int)(address ^ (address >>> 32)); 409 return hash; 410 } 411 412 /** 413 * Compares this value with the given {@code 414 * UserSymbolRecord.Value} and orders by process ID first and 415 * address second. The comparison treats addresses as unsigned 416 * values so the ordering is consistent with that defined in the 417 * native DTrace library. The {@code compareTo()} method is 418 * compatible with {@link #equals(Object o) equals()}. 419 * 420 * @return -1, 0, or 1 as this value's combined process ID and 421 * address is less than, equal to, or greater than the given 422 * value's combined process ID and address 423 */ 424 public int compareTo(Value v)425 compareTo(Value v) 426 { 427 int cmp; 428 cmp = (processID < v.processID ? -1 : 429 (processID > v.processID ? 1 : 0)); 430 if (cmp == 0) { 431 cmp = ProbeData.compareUnsigned(address, v.address); 432 } 433 return cmp; 434 } 435 436 private void readObject(ObjectInputStream s)437 readObject(ObjectInputStream s) 438 throws IOException, ClassNotFoundException 439 { 440 s.defaultReadObject(); 441 // check class invariants 442 try { 443 validate(); 444 } catch (Exception e) { 445 InvalidObjectException x = new InvalidObjectException( 446 e.getMessage()); 447 x.initCause(e); 448 throw x; 449 } 450 } 451 452 /** 453 * Gets a string representation of this {@code 454 * UserSymbolRecord.Value} instance useful for logging and not 455 * intended for display. The exact details of the 456 * representation are unspecified and subject to change, but the 457 * following format may be regarded as typical: 458 * <pre><code> 459 * class-name[property1 = value1, property2 = value2] 460 * </code></pre> 461 */ 462 public String toString()463 toString() 464 { 465 StringBuilder buf = new StringBuilder(); 466 buf.append(Value.class.getName()); 467 buf.append("[processID = "); 468 buf.append(processID); 469 buf.append(", address = "); 470 buf.append(address); 471 buf.append(']'); 472 return buf.toString(); 473 } 474 } 475 } 476