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