UserSymbolRecord.java revision 4ae67516a1d5dc4a5dbc761762bad5b596647388
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* ident "%Z%%M% %I% %E% SMI"
*/
package org.opensolaris.os.dtrace;
import java.io.*;
import java.beans.*;
/**
* A value generated by the DTrace {@code umod()}, {@code ufunc()}, or
* {@code usym()} action used to lookup the symbol associated with a
* user address.
* <p>
* Immutable. Supports persistence using {@link java.beans.XMLEncoder}.
*
* @author Tom Erickson
*/
public final class UserSymbolRecord implements SymbolValueRecord,
Serializable, Comparable <UserSymbolRecord>
{
static final long serialVersionUID = -591954442654439794L;
static {
try {
BeanInfo info = Introspector.getBeanInfo(UserSymbolRecord.class);
PersistenceDelegate persistenceDelegate =
new DefaultPersistenceDelegate(
new String[] {"processID", "symbol", "address"})
{
/*
* Need to prevent DefaultPersistenceDelegate from using
* overridden equals() method, resulting in a
* StackOverFlowError. Revert to PersistenceDelegate
* implementation. See
* http://forum.java.sun.com/thread.jspa?threadID=
* 477019&tstart=135
*/
protected boolean
mutatesTo(Object oldInstance, Object newInstance)
{
return (newInstance != null && oldInstance != null &&
oldInstance.getClass() == newInstance.getClass());
}
};
BeanDescriptor d = info.getBeanDescriptor();
d.setValue("persistenceDelegate", persistenceDelegate);
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
// serialized explicitly to hide implementation; treat as final
private transient Value value;
// set natively after creation; treat as final
private transient String symbol;
/**
* Called by native code.
*/
private
UserSymbolRecord(int pid, long addressValue)
{
value = new Value(pid, addressValue);
}
/**
* Creates a {@code UserSymbolRecord} with the given process ID,
* symbol lookup, and user address converted in probe context as a
* result of the DTrace {@code umod()}, {@code ufunc()}, or {@code
* usym()} action.
* <p>
* Supports XML persistence.
*
* @param pid non-negative user process ID
* @param lookupValue the result in the native DTrace library of
* looking up the symbol associated with the given user address
* @param addressValue symbol address
* @throws NullPointerException if the given lookup value is {@code null}
* @throws IllegalArgumentException if the given process ID is
* negative
*/
public
UserSymbolRecord(int pid, String lookupValue, long addressValue)
{
value = new Value(pid, addressValue);
symbol = lookupValue;
validate();
}
private final void
validate()
{
if (symbol == null) {
throw new NullPointerException("symbol is null");
}
}
/**
* Gets the process ID associated with this record's symbol.
*
* @return non-negative pid
*/
public int
getProcessID()
{
return value.getProcessID();
}
/**
* Gets the result of the address lookup in the same form returned
* by {@link Consumer#lookupUserFunction(int pid, long address)}.
*
* @return non-null address lookup in the format defined by the
* native DTrace library
*/
public String
getSymbol()
{
return symbol;
}
/**
* Called by native code and ProbeData addSymbolRecord()
*/
void
setSymbol(String lookupValue)
{
symbol = lookupValue;
validate();
}
/**
* Gets the symbol's user address.
*
* @return the symbol's user address
*/
public long
getAddress()
{
return value.getAddress();
}
/**
* Gets the composite value of the symbol's process ID and address.
* The value is used in {@link #equals(Object o) equals()} and
* {@link #compareTo(UserSymbolRecord r) compareTo()} to test
* equality and to determine the natural ordering of {@code
* UserSymbolRecord} instances.
*
* @return non-null composite value of the symbols's process ID and
* address
*/
public Value
getValue()
{
return value;
}
/**
* Compares the specified object with this {@code UserSymbolRecord}
* for equality. Returns {@code true} if and only if the specified
* object is also a {@code UserSymbolRecord} and both records have
* the same process ID and the same address.
*
* @return {@code true} if and only if the specified object is also
* a {@code UserSymbolRecord} and both records have the same
* process ID and the same address
*/
@Override
public boolean
equals(Object o)
{
if (o instanceof UserSymbolRecord) {
UserSymbolRecord r = (UserSymbolRecord)o;
return value.equals(r.value);
}
return false;
}
/**
* Overridden to ensure that equal instances have equal hash codes.
*/
@Override
public int
hashCode()
{
return value.hashCode();
}
/**
* Compares this record with the given user symbol lookup and orders
* by the combined value of process ID first and address second.
* The comparison treats addresses as unsigned values so the
* ordering is consistent with that defined in the native DTrace
* library. The {@code compareTo()} method is compatible with {@link
* #equals(Object o) equals()}.
*
* @return -1, 0, or 1 as this record's combined process ID and
* address is less than, equal to, or greater than the given
* record's combined process ID and address
*/
public int
compareTo(UserSymbolRecord r)
{
return value.compareTo(r.value);
}
/**
* Serialize this {@code UserSymbolRecord} instance.
*
* @serialData processID ({@code int}), followed by symbol ({@code
* java.lang.String}), followed by address ({@code long})
*/
private void
writeObject(ObjectOutputStream s) throws IOException
{
s.defaultWriteObject();
s.writeInt(getProcessID());
s.writeObject(getSymbol());
s.writeLong(getAddress());
}
private void
readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
int pid = s.readInt();
symbol = (String)s.readObject();
long addressValue = s.readLong();
try {
value = new Value(pid, addressValue);
validate();
} catch (Exception e) {
InvalidObjectException x = new InvalidObjectException(
e.getMessage());
x.initCause(e);
throw x;
}
}
/**
* Gets the result of this symbol lookup. The format is defined in
* the native DTrace library and is as stable as that library
* definition.
*
* @return {@link #getSymbol()}
*/
@Override
public String
toString()
{
return symbol;
}
/**
* The composite value of a symbol's process ID and user address.
* <p>
* Immutable. Supports persistence using {@link
* java.beans.XMLEncoder}.
*/
public static final class Value implements Serializable,
Comparable <Value> {
static final long serialVersionUID = -91449037747641135L;
static {
try {
BeanInfo info = Introspector.getBeanInfo(
UserSymbolRecord.Value.class);
PersistenceDelegate persistenceDelegate =
new DefaultPersistenceDelegate(
new String[] {"processID", "address"})
{
/*
* Need to prevent DefaultPersistenceDelegate from using
* overridden equals() method, resulting in a
* StackOverFlowError. Revert to PersistenceDelegate
* implementation. See
* http://forum.java.sun.com/thread.jspa?threadID=
* 477019&tstart=135
*/
protected boolean
mutatesTo(Object oldInstance, Object newInstance)
{
return (newInstance != null && oldInstance != null &&
oldInstance.getClass() ==
newInstance.getClass());
}
};
BeanDescriptor d = info.getBeanDescriptor();
d.setValue("persistenceDelegate", persistenceDelegate);
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
/** @serial */
private final int processID;
/** @serial */
private final long address;
/**
* Creates a composite value with the given user process ID and
* symbol address.
* <p>
* Supports XML persistence.
*
* @param pid non-negative process ID
* @param addressValue symbol address
* @throws IllegalArgumentException if the given process ID is
* negative
*/
public
Value(int pid, long addressValue)
{
processID = pid;
address = addressValue;
validate();
}
private final void
validate()
{
if (processID < 0) {
throw new IllegalArgumentException("process ID is negative");
}
}
/**
* Gets the process ID associated with this value's user
* address.
*
* @return non-negative pid
*/
public int
getProcessID()
{
return processID;
}
/**
* Gets the symbol's user address.
*
* @return the symbol's user address
*/
public long
getAddress()
{
return address;
}
/**
* Compares the specified object with this {@code
* UserSymbolRecord.Value} for equality. Returns {@code true}
* if and only if the specified object is also a {@code
* UserSymbolRecord.Value} and both values have the same process
* ID and the same address.
*
* @return {@code true} if and only if the specified object is
* also a {@code UserSymbolRecord.Value} and both values have
* the same process ID and the same address
*/
@Override
public boolean
equals(Object o)
{
if (o instanceof Value) {
Value v = (Value)o;
return ((processID == v.processID) && (address == v.address));
}
return false;
}
/**
* Overridden to ensure that equal instances have equal hash
* codes.
*/
@Override
public int
hashCode()
{
int hash = 17;
hash = 37 * hash + processID;
hash = 37 * hash + (int)(address ^ (address >>> 32));
return hash;
}
/**
* Compares this value with the given {@code
* UserSymbolRecord.Value} and orders by process ID first and
* address second. The comparison treats addresses as unsigned
* values so the ordering is consistent with that defined in the
* native DTrace library. The {@code compareTo()} method is
* compatible with {@link #equals(Object o) equals()}.
*
* @return -1, 0, or 1 as this value's combined process ID and
* address is less than, equal to, or greater than the given
* value's combined process ID and address
*/
public int
compareTo(Value v)
{
int cmp;
cmp = (processID < v.processID ? -1 :
(processID > v.processID ? 1 : 0));
if (cmp == 0) {
cmp = ProbeData.compareUnsigned(address, v.address);
}
return cmp;
}
private void
readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
// check class invariants
try {
validate();
} catch (Exception e) {
InvalidObjectException x = new InvalidObjectException(
e.getMessage());
x.initCause(e);
throw x;
}
}
/**
* Gets a string representation of this {@code
* UserSymbolRecord.Value} instance useful for logging and not
* intended for display. The exact details of the
* representation are unspecified and subject to change, but the
* following format may be regarded as typical:
* <pre><code>
* class-name[property1 = value1, property2 = value2]
* </code></pre>
*/
public String
toString()
{
StringBuilder buf = new StringBuilder();
buf.append(Value.class.getName());
buf.append("[processID = ");
buf.append(processID);
buf.append(", address = ");
buf.append(address);
buf.append(']');
return buf.toString();
}
}
}