root/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelSymbolRecord.java
/*
 * 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 mod()}, {@code func()}, or
 * {@code sym()} action used to lookup the symbol associated with a
 * kernel address.
 * <p>
 * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
 *
 * @author Tom Erickson
 */
public final class KernelSymbolRecord implements SymbolValueRecord,
       Serializable, Comparable <KernelSymbolRecord>
{
    static final long serialVersionUID = -7156627773519296848L;

    static {
        try {
            BeanInfo info = Introspector.getBeanInfo(KernelSymbolRecord.class);
            PersistenceDelegate persistenceDelegate =
                    new DefaultPersistenceDelegate(
                    new String[] {"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();
        }
    }

    /** @serial */
    private String symbol; // set natively after creation; treat as final
    /** @serial */
    private final long address;

    /**
     * Called by native code.
     */
    private
    KernelSymbolRecord(long addressValue)
    {
        address = addressValue;
    }

    /**
     * Creates a {@code KernelSymbolRecord} with the given symbol lookup
     * and kernel address converted in probe context as a result of the
     * DTrace {@code mod()}, {@code func()}, or {@code sym()} action.
     * <p>
     * Supports XML persistence.
     *
     * @param addressValue symbol address
     * @param lookupValue the result in the native DTrace library of
     * looking up the symbol associated with the given kernel address
     * @throws NullPointerException if the given lookup value is {@code null}
     */
    public
    KernelSymbolRecord(String lookupValue, long addressValue)
    {
        symbol = lookupValue;
        address = addressValue;
        validate();
    }

    private final void
    validate()
    {
        if (symbol == null) {
            throw new NullPointerException("symbol is null");
        }
    }

    /**
     * Gets the result of the address lookup in the same form returned
     * by {@link Consumer#lookupKernelFunction(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 kernel address.
     *
     * @return the symbol's kernel address
     */
    public long
    getAddress()
    {
        return address;
    }

    /**
     * Gets the symbol's kernel address.  The value is used in {@link
     * #equals(Object o) equals()} and {@link
     * #compareTo(KernelSymbolRecord r) compareTo()} to test equality
     * and to determine the natural ordering of {@code
     * KernelSymbolRecord} instances.
     *
     * @return non-null value of the symbol's kernel address
     */
    public Long
    getValue()
    {
        return address;
    }

    /**
     * Compares the specified object with this {@code KernelSymbolRecord}
     * for equality.  Returns {@code true} if and only if the specified
     * object is also a {@code KernelSymbolRecord} and both records have
     * the same address.
     *
     * @return {@code true} if and only if the specified object is also
     * a {@code KernelSymbolRecord} and both records have the same
     * address
     */
    @Override
    public boolean
    equals(Object o)
    {
        if (o instanceof KernelSymbolRecord) {
            KernelSymbolRecord r = (KernelSymbolRecord)o;
            return (address == r.address);
        }
        return false;
    }

    /**
     * Overridden to ensure that equal instances have equal hash codes.
     */
    @Override
    public int
    hashCode()
    {
        return (int)(address ^ (address >>> 32));
    }

    /**
     * Compares this record with the given kernel symbol lookup and
     * orders by address.  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 address is less than, equal
     * to, or greater than the given record's address
     */
    public int
    compareTo(KernelSymbolRecord r)
    {
        return ProbeData.compareUnsigned(address, r.address);
    }

    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 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;
    }
}