xref: /illumos-gate/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/UserSymbolRecord.java (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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