xref: /illumos-gate/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/KernelStackRecord.java (revision 445f2479fe3d7435daab18bf2cdc310b86cd6738)
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.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[]
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      */
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
159     KernelStackRecord(StackFrame[] frames, byte[] rawBytes)
160     {
161 	if (frames != null) {
162 	    stackFrames = (StackFrame[])frames.clone();
163 	}
164 	if (rawBytes != null) {
165 	    rawStackData = (byte[])rawBytes.clone();
166 	}
167 	validate();
168     }
169 
170     private void
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[]
188     getStackFrames()
189     {
190 	if (stackFrames == null) {
191 	    return EMPTY_FRAMES;
192 	}
193 	return (StackFrame[])stackFrames.clone();
194     }
195 
196     /**
197      * Called by native code and by UserStackRecord in its
198      * setStackFrames() method.
199      */
200     void
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[]
220     getRawStackData()
221     {
222 	return (byte[])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
233     getValue()
234     {
235 	return (byte[])rawStackData.clone();
236     }
237 
238     public List <StackFrame>
239     asList()
240     {
241 	if (stackFrames == null) {
242 	    return Collections. <StackFrame> emptyList();
243 	}
244 	return Collections.unmodifiableList(Arrays.asList(stackFrames));
245     }
246 
247     /**
248      * Compares the specified object with this {@code KernelStackRecord}
249      * for equality.  Returns {@code true} if and only if the specified
250      * object is also a {@code KernelStackRecord} and both records have
251      * the same raw stack data.
252      * <p>
253      * This implementation first checks if the specified object is this
254      * {@code KernelStackRecord}.  If so, it returns {@code true}.
255      *
256      * @return {@code true} if and only if the specified object is also
257      * a {@code KernelStackRecord} and both records have the same raw
258      * stack data
259      */
260     @Override
261     public boolean
262     equals(Object o)
263     {
264 	if (o == this) {
265 	    return true;
266 	}
267 	if (o instanceof KernelStackRecord) {
268 	    KernelStackRecord r = (KernelStackRecord)o;
269 	    return Arrays.equals(rawStackData, r.rawStackData);
270 	}
271 	return false;
272     }
273 
274     /**
275      * Overridden to ensure that equal instances have equal hash codes.
276      */
277     @Override
278     public int
279     hashCode()
280     {
281 	return Arrays.hashCode(rawStackData);
282     }
283 
284     /**
285      * Compares this record with the given stack record.  Compares the
286      * first unequal pair of bytes at the same index in each record's
287      * raw stack data, or if all corresponding bytes are equal, compares
288      * the length of each record's array of raw stack data.  The {@code
289      * compareTo()} method is compatible with {@link #equals(Object o)
290      * equals()}.
291      * <p>
292      * This implementation first checks if the specified record is this
293      * {@code KernelStackRecord}.  If so, it returns {@code 0}.
294      *
295      * @return -1, 0, or 1 as this record's raw stack data is less than,
296      * equal to, or greater than the given record's raw stack data.
297      */
298     public int
299     compareTo(KernelStackRecord r)
300     {
301 	if (r == this) {
302 	    return 0;
303 	}
304 
305 	int len1 = rawStackData.length;
306 	int len2 = r.rawStackData.length;
307 	int cmp = 0;
308 	for (int i = 0; (cmp == 0) && (i < len1) && (i < len2); ++i) {
309 	    cmp = ((rawStackData[i] < r.rawStackData[i]) ? -1 :
310 		    ((rawStackData[i] > r.rawStackData[i]) ? 1 : 0));
311 	}
312 	if (cmp == 0) {
313 	    cmp = ((len1 < len2) ? -1 : ((len1 > len2) ? 1 : 0));
314 	}
315 	return cmp;
316     }
317 
318     private void
319     readObject(ObjectInputStream s)
320             throws IOException, ClassNotFoundException
321     {
322 	s.defaultReadObject();
323 	// Make a defensive copy of stack frames and raw bytes
324 	if (stackFrames != null) {
325 	    stackFrames = (StackFrame[])stackFrames.clone();
326 	}
327 	if (rawStackData != null) {
328 	    rawStackData = (byte[])rawStackData.clone();
329 	}
330 	// check class invariants
331 	try {
332 	    validate();
333 	} catch (Exception e) {
334 	    throw new InvalidObjectException(e.getMessage());
335 	}
336     }
337 
338     /**
339      * Gets a multi-line string representation of a stack with one frame
340      * per line.  Each line is of the format {@code
341      * module`function+offset}, where {@code module} and {@code
342      * function} are symbol names and offset is a hex integer preceded
343      * by {@code 0x}.  For example: {@code genunix`open+0x19}.  The
344      * offset (and the preceding '+' sign) are omitted if offset is
345      * zero.  If function name lookup fails, the raw pointer value is
346      * used instead.  In that case, the module name (and the `
347      * delimiter) may or may not be present, depending on whether or not
348      * module lookup also fails, and a raw pointer value appears in
349      * place of {@code function+offset} as a hex value preceded by
350      * {@code 0x}.  The format just described, not including surrounding
351      * whitespce, is defined in the native DTrace library and is as
352      * stable as that library definition.  Each line is indented by an
353      * equal (unspecified) number of spaces.
354      * <p>
355      * If human-readable stack frames are not available (see {@link
356      * #getStackFrames()}), a table represenation of {@link
357      * #getRawStackData()} is returned instead.  The table displays 16
358      * bytes per row in unsigned hex followed by the ASCII character
359      * representations of those bytes.  Each unprintable character is
360      * represented by a period (.).
361      */
362     @Override
363     public String
364     toString()
365     {
366 	StackFrame[] frames = getStackFrames();
367 	if (frames.length == 0) {
368 	    return ScalarRecord.rawBytesString(rawStackData);
369 	}
370 
371 	StringBuffer buf = new StringBuffer();
372 	buf.append('\n');
373 	for (StackFrame f : frames) {
374 	    for (int i = 0; i < STACK_INDENT; ++i) {
375 		buf.append(' ');
376 	    }
377 	    buf.append(f);
378 	    buf.append('\n');
379 	}
380 	return buf.toString();
381     }
382 }
383