xref: /titanic_41/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java (revision e77b06d21580f630e0a7c437495ab283d3672828)
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 2008 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.util.*;
31 import java.io.*;
32 import java.util.regex.Pattern;
33 import java.beans.*;
34 
35 /**
36  * A value generated by the DTrace {@code stack()} action.
37  * <p>
38  * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
39  *
40  * @author Tom Erickson
41  */
42 public final class KernelStackRecord implements StackValueRecord,
43        Serializable, Comparable <KernelStackRecord>
44 {
45     static final long serialVersionUID = 8616454544771346573L;
46     static final int STACK_INDENT = 14;
47     static final StackFrame[] EMPTY_FRAMES = new StackFrame[] {};
48 
49     static {
50 	try {
51 	    BeanInfo info = Introspector.getBeanInfo(KernelStackRecord.class);
52 	    PersistenceDelegate persistenceDelegate =
53 		    new DefaultPersistenceDelegate(
54 		    new String[] {"stackFrames", "rawStackData"})
55 	    {
56 		/*
57 		 * Need to prevent DefaultPersistenceDelegate from using
58 		 * overridden equals() method, resulting in a
59 		 * StackOverFlowError.  Revert to PersistenceDelegate
60 		 * implementation.  See
61 		 * http://forum.java.sun.com/thread.jspa?threadID=
62 		 * 477019&tstart=135
63 		 */
64 		protected boolean
65 		mutatesTo(Object oldInstance, Object newInstance)
66 		{
67 		    return (newInstance != null && oldInstance != null &&
68 			    oldInstance.getClass() == newInstance.getClass());
69 		}
70 	    };
71 	    BeanDescriptor d = info.getBeanDescriptor();
72 	    d.setValue("persistenceDelegate", persistenceDelegate);
73 	} catch (IntrospectionException e) {
74 	    System.out.println(e);
75 	}
76     }
77 
78     /**
79      * Splits formatted call stack generated by DTrace stack() and
80      * ustack() actions into tokens delimited by whitespace.  Matches
81      * any number of whitespace characters on either side of a newline.
82      * Can't assume that a line has no whitespace characters. A java
83      * stack might have the line "StubRoutines (1)", which must not get
84      * split into two tokens.
85      */
86     static final Pattern STACK_TOKENIZER = Pattern.compile("\\s*\n\\s*");
87 
88     /**
89      * Called by JNI layer to convert a stack formatted by the native
90      * DTrace library into an unformatted array of stack frames.
91      *
92      * @param s  string representation of stack data generated by the D
93      * {@code stack()}, {@code ustack()}, or {@code jstack()} action
94      * @return array of human-readable stack frames
95      */
96     static StackFrame[]
parse(String s)97     parse(String s)
98     {
99 	//
100 	// First trim the leading whitespace to avoid an initial empty
101 	// element in the returned array.
102 	//
103 	s = s.trim();
104 	StackFrame[] frames;
105 	if (s.length() == 0) {
106 	    frames = EMPTY_FRAMES;
107 	} else {
108 	    String[] f = STACK_TOKENIZER.split(s);
109 	    int n = f.length;
110 	    frames = new StackFrame[n];
111 	    for (int i = 0; i < n; ++i) {
112 		frames[i] = new StackFrame(f[i]);
113 	    }
114 	}
115 	return frames;
116     }
117 
118     /** @serial */
119     private StackFrame[] stackFrames;
120     /** @serial */
121     private byte[] rawStackData;
122 
123     /**
124      * Called by native code and by UserStackRecord (in its constructor
125      * called by native code).
126      *
127      * @throws NullPointerException if rawBytes is {@code null}
128      */
KernelStackRecord(byte[] rawBytes)129     KernelStackRecord(byte[] rawBytes)
130     {
131 	// No need for defensive copy; native code will not modify input
132 	// raw bytes.
133 	rawStackData = rawBytes;
134 	if (rawStackData == null) {
135 	    throw new NullPointerException("raw stack data is null");
136 	}
137     }
138 
139     /**
140      * Creates a {@code KernelStackRecord} with the given stack frames
141      * and raw stack data.  Supports XML persistence.
142      *
143      * @param frames array of human-readable stack frames, copied so
144      * that later modifying the given frames array will not affect this
145      * {@code KernelStackRecord}; may be {@code null} or empty to
146      * indicate that the raw stack data was not converted to
147      * human-readable stack frames (see {@link
148      * StackValueRecord#getStackFrames()})
149      * @param rawBytes array of raw bytes used to represent this stack
150      * value in the native DTrace library, needed to distinguish stacks
151      * that have the same display value but are considered distinct by
152      * DTrace; copied so that later modifying the given array will not
153      * affect this {@code KernelStackRecord}
154      * @throws NullPointerException if the given array of raw bytes is
155      * {@code null} or if any element of the {@code frames} array is
156      * {@code null}
157      */
158     public
KernelStackRecord(StackFrame[] frames, byte[] rawBytes)159     KernelStackRecord(StackFrame[] frames, byte[] rawBytes)
160     {
161 	if (frames != null) {
162 	    stackFrames = frames.clone();
163 	}
164 	if (rawBytes != null) {
165 	    rawStackData = rawBytes.clone();
166 	}
167 	validate();
168     }
169 
170     private final void
validate()171     validate()
172     {
173 	if (rawStackData == null) {
174 	    throw new NullPointerException("raw stack data is null");
175 	}
176 	// stackFrames may be null; if non-null, cannot contain null
177 	// elements
178 	if (stackFrames != null) {
179 	    for (StackFrame f : stackFrames) {
180 		if (f == null) {
181 		    throw new NullPointerException("stack frame is null");
182 		}
183 	    }
184 	}
185     }
186 
187     public StackFrame[]
getStackFrames()188     getStackFrames()
189     {
190 	if (stackFrames == null) {
191 	    return EMPTY_FRAMES;
192 	}
193 	return stackFrames.clone();
194     }
195 
196     /**
197      * Called by native code and by UserStackRecord in its
198      * setStackFrames() method.
199      */
200     void
setStackFrames(StackFrame[] frames)201     setStackFrames(StackFrame[] frames)
202     {
203 	// No need for defensive copy; native code will not modify input
204 	// frames.
205 	stackFrames = frames;
206 	validate();
207     }
208 
209     /**
210      * Gets the native DTrace representation of this record's stack as
211      * an array of raw bytes.  The raw bytes are used in {@link
212      * #equals(Object o) equals()} and {@link
213      * #compareTo(KernelStackRecord r) compareTo()} to test equality and
214      * to determine the natural ordering of kernel stack records.
215      *
216      * @return the native DTrace library's internal representation of
217      * this record's stack as a non-null array of bytes
218      */
219     public byte[]
getRawStackData()220     getRawStackData()
221     {
222 	return rawStackData.clone();
223     }
224 
225     /**
226      * Gets the raw bytes used to represent this record's stack value in
227      * the native DTrace library.  To get a human-readable
228      * representation, call {@link #toString()}.
229      *
230      * @return {@link #getRawStackData()}
231      */
232     public Object
getValue()233     getValue()
234     {
235 	return rawStackData.clone();
236     }
237 
238     public List <StackFrame>
asList()239     asList()
240     {
241 	if (stackFrames == null) {
242 	    return Collections. <StackFrame> emptyList();
243 	}
244 	return Collections. <StackFrame> unmodifiableList(
245 		Arrays.asList(stackFrames));
246     }
247 
248     /**
249      * Compares the specified object with this {@code KernelStackRecord}
250      * for equality.  Returns {@code true} if and only if the specified
251      * object is also a {@code KernelStackRecord} and both records have
252      * the same raw stack data.
253      * <p>
254      * This implementation first checks if the specified object is this
255      * {@code KernelStackRecord}.  If so, it returns {@code true}.
256      *
257      * @return {@code true} if and only if the specified object is also
258      * a {@code KernelStackRecord} and both records have the same raw
259      * stack data
260      */
261     @Override
262     public boolean
equals(Object o)263     equals(Object o)
264     {
265 	if (o == this) {
266 	    return true;
267 	}
268 	if (o instanceof KernelStackRecord) {
269 	    KernelStackRecord r = (KernelStackRecord)o;
270 	    return Arrays.equals(rawStackData, r.rawStackData);
271 	}
272 	return false;
273     }
274 
275     /**
276      * Overridden to ensure that equal instances have equal hash codes.
277      */
278     @Override
279     public int
hashCode()280     hashCode()
281     {
282 	return Arrays.hashCode(rawStackData);
283     }
284 
285     /**
286      * Compares this record with the given {@code KernelStackRecord}.
287      * Compares the first unequal pair of bytes at the same index in
288      * each record's raw stack data, or if all corresponding bytes are
289      * equal, compares the length of each record's array of raw stack
290      * data.  Corresponding bytes are compared as unsigned values.  The
291      * {@code compareTo()} method is compatible with {@link
292      * #equals(Object o) equals()}.
293      * <p>
294      * This implementation first checks if the specified record is this
295      * {@code KernelStackRecord}.  If so, it returns {@code 0}.
296      *
297      * @return -1, 0, or 1 as this record's raw stack data is less than,
298      * equal to, or greater than the given record's raw stack data.
299      */
300     public int
compareTo(KernelStackRecord r)301     compareTo(KernelStackRecord r)
302     {
303 	if (r == this) {
304 	    return 0;
305 	}
306 
307 	return ProbeData.compareByteArrays(rawStackData, r.rawStackData);
308     }
309 
310     private void
readObject(ObjectInputStream s)311     readObject(ObjectInputStream s)
312             throws IOException, ClassNotFoundException
313     {
314 	s.defaultReadObject();
315 	// Make a defensive copy of stack frames and raw bytes
316 	if (stackFrames != null) {
317 	    stackFrames = stackFrames.clone();
318 	}
319 	if (rawStackData != null) {
320 	    rawStackData = rawStackData.clone();
321 	}
322 	// check class invariants
323 	try {
324 	    validate();
325 	} catch (Exception e) {
326 	    InvalidObjectException x = new InvalidObjectException(
327 		    e.getMessage());
328 	    x.initCause(e);
329 	    throw x;
330 	}
331     }
332 
333     /**
334      * Gets a multi-line string representation of a stack with one frame
335      * per line.  Each line is of the format {@code
336      * module`function+offset}, where {@code module} and {@code
337      * function} are symbol names and offset is a hex integer preceded
338      * by {@code 0x}.  For example: {@code genunix`open+0x19}.  The
339      * offset (and the preceding '+' sign) are omitted if offset is
340      * zero.  If function name lookup fails, the raw pointer value is
341      * used instead.  In that case, the module name (and the `
342      * delimiter) may or may not be present, depending on whether or not
343      * module lookup also fails, and a raw pointer value appears in
344      * place of {@code function+offset} as a hex value preceded by
345      * {@code 0x}.  The format just described, not including surrounding
346      * whitespce, is defined in the native DTrace library and is as
347      * stable as that library definition.  Each line is indented by an
348      * equal (unspecified) number of spaces.
349      * <p>
350      * If human-readable stack frames are not available (see {@link
351      * #getStackFrames()}), a table represenation of {@link
352      * #getRawStackData()} is returned instead.  The table displays 16
353      * bytes per row in unsigned hex followed by the ASCII character
354      * representations of those bytes.  Each unprintable character is
355      * represented by a period (.).
356      */
357     @Override
358     public String
toString()359     toString()
360     {
361 	StackFrame[] frames = getStackFrames();
362 	if (frames.length == 0) {
363 	    return ScalarRecord.rawBytesString(rawStackData);
364 	}
365 
366 	StringBuilder buf = new StringBuilder();
367 	buf.append('\n');
368 	for (StackFrame f : frames) {
369 	    for (int i = 0; i < STACK_INDENT; ++i) {
370 		buf.append(' ');
371 	    }
372 	    buf.append(f);
373 	    buf.append('\n');
374 	}
375 	return buf.toString();
376     }
377 }
378