xref: /illumos-gate/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/Flow.java (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  * Description of control flow across function boundaries including
35  * direction (entry or return) and depth in the call stack.  This
36  * information is added to {@link ProbeData} instances only when the
37  * {@link Option#flowindent flowindent} option is used:
38  * <pre><code>
39  *     Consumer consumer = new LocalConsumer();
40  *     consumer.open();
41  *     consumer.setOption(Option.flowindent);
42  *     ...
43  * </code></pre>
44  * See the <a
45  * href="http://docs.sun.com/app/docs/doc/817-6223/6mlkidlk1?a=view">
46  * <b>Examples</b></a> section of the <b>{@code fbt}
47  * Provider</b> chapter of the <i>Solaris Dynamic Tracing Guide</i>.
48  * <p>
49  * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
50  *
51  * @see Consumer#setOption(String option)
52  * @see Option#flowindent
53  *
54  * @author Tom Erickson
55  */
56 public final class Flow implements Serializable {
57     static final long serialVersionUID = -9178272444872063901L;
58 
59     /**
60      * Indicates direction of flow across a boundary, such as entering
61      * or returing from a function.
62      */
63     public enum Kind {
64 	/** Entry into a function. */
65 	ENTRY,
66 	/** Return from a function. */
67 	RETURN,
68 	/** No function boundary crossed. */
69 	NONE
70     }
71 
72     static {
73 	try {
74 	    BeanInfo info = Introspector.getBeanInfo(Flow.class);
75 	    PersistenceDelegate persistenceDelegate =
76 		    new DefaultPersistenceDelegate(
77 		    new String[] {"kind", "depth"})
78 	    {
79 		/*
80 		 * Need to prevent DefaultPersistenceDelegate from using
81 		 * overridden equals() method, resulting in a
82 		 * StackOverFlowError.  Revert to PersistenceDelegate
83 		 * implementation.  See
84 		 * http://forum.java.sun.com/thread.jspa?threadID=
85 		 * 477019&tstart=135
86 		 */
87 		protected boolean
88 		mutatesTo(Object oldInstance, Object newInstance)
89 		{
90 		    return (newInstance != null && oldInstance != null &&
91 			    oldInstance.getClass() == newInstance.getClass());
92 		}
93 
94 		protected Expression
95 		instantiate(Object oldInstance, Encoder out)
96 		{
97 		    Flow flow = (Flow)oldInstance;
98 		    return new Expression(oldInstance, oldInstance.getClass(),
99 			    "new", new Object[] { flow.getKind().name(),
100 			    flow.getDepth() });
101 		}
102 	    };
103 	    BeanDescriptor d = info.getBeanDescriptor();
104 	    d.setValue("persistenceDelegate", persistenceDelegate);
105 	} catch (IntrospectionException e) {
106 	    e.printStackTrace();
107 	}
108     }
109 
110     /** @serial */
111     private final Kind kind;
112     /** @serial */
113     private final int depth;
114 
115     /**
116      * Creates a {@code Flow} instance with the given flow kind and
117      * depth.  Supports XML persistence.
118      *
119      * @param flowKindName name of enumeration value indicating the
120      * direction of flow
121      * @param flowDepth current depth in the call stack
122      * @throws IllegalArgumentException if there is no {@code Flow.Kind}
123      * value with the given name or if the given {@code flowDepth} is
124      * negative
125      * @throws NullPointerException if the given {@code Flow.Kind} name
126      * is {@code null}
127      */
128     public
129     Flow(String flowKindName, int flowDepth)
130     {
131 	kind = Enum.valueOf(Kind.class, flowKindName);
132 	depth = flowDepth;
133 	if (depth < 0) {
134 	    throw new IllegalArgumentException("depth is negative");
135 	}
136     }
137 
138     /**
139      * Gets the direction of the flow of control (entry or return)
140      * across a function boundary.
141      *
142      * @return non-null flow kind indicating direction of flow (entry or
143      * return) across a function boundary
144      */
145     public Kind
146     getKind()
147     {
148 	return kind;
149     }
150 
151     /**
152      * Gets the current depth in the call stack.
153      *
154      * @return A non-negative sum of the function entries minus the
155      * function returns up until the moment described by this control
156      * flow instance.  For example, if the traced flow of control
157      * entered two functions but only returned from one, the depth is
158      * one (2 entries minus 1 return).
159      */
160     public int
161     getDepth()
162     {
163 	return depth;
164     }
165 
166     /**
167      * Compares the specified object with this {@code Flow} instance for
168      * equality.  Defines equality as having the same flow kind and
169      * depth.
170      *
171      * @return {@code true} if and only if the specified object is of
172      * type {@code Flow} and both instances have equal flow kind and
173      * depth.
174      */
175     @Override
176     public boolean
177     equals(Object o)
178     {
179 	if (o instanceof Flow) {
180 	    Flow f = (Flow)o;
181 	    return ((kind == f.kind) && (depth == f.depth));
182 	}
183 	return false;
184     }
185 
186     /**
187      * Overridden to ensure that equal instances have equal hash codes.
188      */
189     @Override
190     public int
191     hashCode()
192     {
193 	int hash = 17;
194 	hash = (37 * hash) + kind.hashCode();
195 	hash = (37 * hash) + depth;
196 	return hash;
197     }
198 
199     private void
200     readObject(ObjectInputStream s)
201 	    throws IOException, ClassNotFoundException
202     {
203 	s.defaultReadObject();
204 	// check class invariants
205 	if (kind == null) {
206 	    throw new InvalidObjectException("kind is null");
207 	}
208 	if (depth < 0) {
209 	    throw new InvalidObjectException("depth is negative");
210 	}
211     }
212 
213     /**
214      * Gets a string representation of this {@code Flow} instance useful
215      * for logging and not intended for display.  The exact details of
216      * the representation are unspecified and subject to change, but the
217      * following format may be regarded as typical:
218      * <pre><code>
219      * class-name[property1 = value1, property2 = value2]
220      * </code></pre>
221      */
222     public String
223     toString()
224     {
225 	StringBuilder buf = new StringBuilder();
226 	buf.append(Flow.class.getName());
227 	buf.append("[kind = ");
228 	buf.append(kind);
229 	buf.append(", depth = ");
230 	buf.append(depth);
231 	buf.append(']');
232 	return buf.toString();
233     }
234 }
235