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