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