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