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.text.ParseException; 32 import java.io.*; 33 import java.beans.*; 34 35 /** 36 * A DTrace probe description consists of provider, module, function, 37 * and name. A single probe description may identify a single DTrace 38 * probe or match multiple probes. Any field may be wildcarded by 39 * omission (set to null) or set to a glob-style pattern: 40 * <pre> 41 * * Matches any string, including the null string 42 * ? Matches any single character 43 * [ ... ] Matches any one of the enclosed characters. A pair of 44 * characters separated by - matches any character 45 * between the pair, inclusive. If the first 46 * character after the [ is !, any character not 47 * enclosed in the set is matched. 48 * \ Interpret the next character as itself, without any 49 * special meaning 50 * </pre> 51 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 52 * 53 * @see Consumer#listProbes(ProbeDescription filter) 54 * 55 * @author Tom Erickson 56 */ 57 public final class ProbeDescription implements Serializable, 58 Comparable <ProbeDescription> 59 { 60 static final long serialVersionUID = 5978023304364513667L; 61 62 /** 63 * Instance with empty provider, module, function, and name fields 64 * matches all DTrace probes on a system. 65 */ 66 public static final ProbeDescription EMPTY = 67 new ProbeDescription(null, null, null, null); 68 69 private static final int ID_NONE = -1; 70 71 /** 72 * Enumerates the provider, module, function, and name fields of a 73 * probe description. 74 */ 75 public enum Spec { 76 /** Probe provider */ 77 PROVIDER, 78 /** Probe module */ 79 MODULE, 80 /** Probe function */ 81 FUNCTION, 82 /** Probe name (unqualified) */ 83 NAME 84 }; 85 86 static { 87 try { 88 BeanInfo info = Introspector.getBeanInfo(ProbeDescription.class); 89 PersistenceDelegate persistenceDelegate = 90 new DefaultPersistenceDelegate( 91 new String[] {"id", "provider", "module", 92 "function", "name"}); 93 BeanDescriptor d = info.getBeanDescriptor(); 94 d.setValue("persistenceDelegate", persistenceDelegate); 95 } catch (IntrospectionException e) { 96 System.out.println(e); 97 } 98 } 99 100 /** @serial */ 101 private int id = ID_NONE; // set by native code 102 103 /** @serial */ 104 private final String provider; 105 /** @serial */ 106 private final String module; 107 /** @serial */ 108 private final String function; 109 /** @serial */ 110 private final String name; 111 112 /** 113 * Creates a probe description that specifies only the unqualified 114 * probe name. 115 * 116 * @see ProbeDescription#ProbeDescription(String probeProvider, 117 * String probeModule, String probeFunction, String probeName) 118 */ 119 public 120 ProbeDescription(String probeName) 121 { 122 this(null, null, null, probeName); 123 } 124 125 /** 126 * Creates a probe description that specifies the probe name 127 * qualified only by the function name. 128 * 129 * @see ProbeDescription#ProbeDescription(String probeProvider, 130 * String probeModule, String probeFunction, String probeName) 131 */ 132 public 133 ProbeDescription(String probeFunction, String probeName) 134 { 135 this(null, null, probeFunction, probeName); 136 } 137 138 /** 139 * Creates a probe description that specifies the probe name 140 * qualified by the function name and module name. 141 * 142 * @see ProbeDescription#ProbeDescription(String probeProvider, 143 * String probeModule, String probeFunction, String probeName) 144 */ 145 public 146 ProbeDescription(String probeModule, String probeFunction, 147 String probeName) 148 { 149 this(null, probeModule, probeFunction, probeName); 150 } 151 152 /** 153 * Creates a fully qualified probe description. If no pattern 154 * syntax is used and no field is omitted, the resulting description 155 * matches at most one DTrace probe. 156 * 157 * @param probeProvider provider name, may be null or empty to match 158 * all providers or use pattern syntax to match multiple providers 159 * @param probeModule module name, may be null or empty to match all 160 * modules or use pattern syntax to match multiple modules 161 * @param probeFunction function name, may be null or empty to match 162 * all functions or use pattern syntax to match multiple functions 163 * @param probeName unqualified probe name, may be null or empty to 164 * match all names or use pattern syntax to match multiple names 165 */ 166 public 167 ProbeDescription(String probeProvider, 168 String probeModule, 169 String probeFunction, 170 String probeName) 171 { 172 provider = ((probeProvider == null) ? "" : probeProvider); 173 module = ((probeModule == null) ? "" : probeModule); 174 function = ((probeFunction == null) ? "" : probeFunction); 175 name = ((probeName == null) ? "" : probeName); 176 } 177 178 /** 179 * Supports XML persistence. 180 */ 181 public 182 ProbeDescription(int probeID, 183 String probeProvider, 184 String probeModule, 185 String probeFunction, 186 String probeName) 187 { 188 this(probeProvider, probeModule, probeFunction, probeName); 189 id = probeID; 190 } 191 192 /** 193 * Generates a probe description from a string in the same format 194 * returned by {@link #toString()}. Parses the string from right to 195 * left. 196 * <pre><code> 197 * <i>provider:module:function:name</i> 198 * </code></pre> 199 * 200 * @return non-null probe description 201 * @throws ParseException if {@code s} does not have the expected 202 * format. The error offset is the index of the first unexpected 203 * character encountered starting from the last character and 204 * reading backwards. 205 * @throws NullPointerException if the given string is {@code null} 206 */ 207 public static ProbeDescription 208 parse(String s) throws ParseException 209 { 210 ProbeDescription p; 211 212 // StringTokenizer and String.split() do not correctly handle 213 // the case of consecutive delimiters 214 List <String> list = new ArrayList <String> (); 215 int len = s.length(); 216 int npos = len; 217 char ch; 218 for (int i = (len - 1); i >= 0; --i) { 219 ch = s.charAt(i); 220 if (ch == ':') { 221 list.add(0, s.substring((i + 1), npos)); 222 npos = i; 223 } 224 } 225 list.add(0, s.substring(0, npos)); 226 227 switch (list.size()) { 228 case 0: 229 p = EMPTY; 230 break; 231 case 1: 232 p = new ProbeDescription(list.get(0)); 233 break; 234 case 2: 235 p = new ProbeDescription(list.get(0), list.get(1)); 236 break; 237 case 3: 238 p = new ProbeDescription(list.get(0), list.get(1), 239 list.get(2)); 240 break; 241 case 4: 242 p = new ProbeDescription(list.get(0), list.get(1), 243 list.get(2), list.get(3)); 244 break; 245 default: 246 // get error offset (parsing right-to-left) 247 int offset = (s.length() - 4); 248 len = list.size(); 249 for (int i = (len - 1); i >= (len - 4); --i) { 250 offset -= list.get(i).length(); 251 } 252 throw new ParseException("Overspecified probe " + 253 "description: \"" + s + "\"", offset); 254 } 255 return p; 256 } 257 258 /** 259 * Gets the probe ID. 260 * 261 * @return ID generated from a sequence by the native DTrace 262 * library, identifies the probe among all probes on the system 263 */ 264 public int 265 getID() 266 { 267 return id; 268 } 269 270 /** 271 * Gets the provider name. 272 * 273 * @return non-null provider name, may be an empty string to 274 * indicate omission 275 */ 276 public String 277 getProvider() 278 { 279 return provider; 280 } 281 282 /** 283 * Gets the module name. 284 * 285 * @return non-null module name, may be an empty string to indicate 286 * omission 287 */ 288 public String 289 getModule() 290 { 291 return module; 292 } 293 294 /** 295 * Gets the function name. 296 * 297 * @return non-null function name, may be an empty string to 298 * indicate omission 299 */ 300 public String 301 getFunction() 302 { 303 return function; 304 } 305 306 /** 307 * Gets the unqualified probe name. 308 * 309 * @return non-null probe name, may be an empty string to indicate 310 * omission 311 */ 312 public String 313 getName() 314 { 315 return name; 316 } 317 318 /** 319 * Returns {@code true} if provider, module, function, and name are 320 * all omitted. An empty probe description matches all DTrace 321 * probes on a system. 322 * 323 * @return {@code true} if all probe fields are omitted, {@code 324 * false} otherwise 325 */ 326 public boolean 327 isEmpty() 328 { 329 if (provider.length() > 0) { 330 return false; 331 } 332 if (module.length() > 0) { 333 return false; 334 } 335 if (function.length() > 0) { 336 return false; 337 } 338 if (name.length() > 0) { 339 return false; 340 } 341 return true; 342 } 343 344 /** 345 * Compares the specified object with this probe description for 346 * equality. Defines equality as having the same fields. Omitted 347 * fields must be omitted in both instances in order for them to be 348 * equal, but it makes no difference whether {@code null} or empty 349 * string was used to indicate omission. 350 * 351 * @return {@code true} if and only if all corresponding fields of 352 * both probe descriptions are either both omitted (null or empty) 353 * or else equal as defined by {@link String#equals(Object o) 354 * String.equals()} 355 */ 356 public boolean 357 equals(Object o) 358 { 359 if (o instanceof ProbeDescription) { 360 ProbeDescription p = (ProbeDescription)o; 361 if ((id == ID_NONE) || (p.id == ID_NONE)) { 362 return (compareTo(p) == 0); 363 } else { 364 return (id == p.id); 365 } 366 } 367 368 return false; 369 } 370 371 /** 372 * Defines the natural ordering of probe descriptions. Returns the 373 * natural ordering of the first unequal pair of corresponding 374 * fields (starting with the provider and continuing to the 375 * unqualified name only if all other fields are equal). 376 * Corresponding fields are equal if they are both omitted or both 377 * equal as defined by {@link String#equals(Object o) 378 * String.equals()}. It makes no difference if {@code null} or 379 * empty string is used to indicate omission. The behavior is 380 * consistent with the {@link #equals(Object o) equals()} method. 381 * 382 * @return -1, 0, or 1 as this probe description is less than, equal 383 * to, or greater than the given probe description 384 */ 385 public int 386 compareTo(ProbeDescription p) 387 { 388 int cmp = 0; 389 cmp = provider.compareTo(p.provider); 390 if (cmp == 0) { 391 cmp = module.compareTo(p.module); 392 if (cmp == 0) { 393 cmp = function.compareTo(p.function); 394 if (cmp == 0) { 395 cmp = name.compareTo(p.name); 396 } 397 } 398 } 399 return (cmp); 400 } 401 402 /** 403 * Overridden to ensure that equal probe descriptions have equal 404 * hashcodes. 405 */ 406 @Override 407 public int 408 hashCode() 409 { 410 int hash = id; 411 if (hash != ID_NONE) { 412 return hash; 413 } 414 415 hash = 17; 416 hash = (37 * hash) + provider.hashCode(); 417 hash = (37 * hash) + module.hashCode(); 418 hash = (37 * hash) + function.hashCode(); 419 hash = (37 * hash) + name.hashCode(); 420 return hash; 421 } 422 423 private void 424 readObject(ObjectInputStream s) 425 throws IOException, ClassNotFoundException 426 { 427 s.defaultReadObject(); 428 // check invariants 429 if (provider == null) { 430 throw new InvalidObjectException("provider is null"); 431 } 432 if (module == null) { 433 throw new InvalidObjectException("module is null"); 434 } 435 if (function == null) { 436 throw new InvalidObjectException("function is null"); 437 } 438 if (name == null) { 439 throw new InvalidObjectException("name is null"); 440 } 441 } 442 443 /** 444 * Gets the string representation of this probe description. The 445 * format is as follows: 446 * <pre><code> 447 * <i>provider:module:function:name</i> 448 * </code></pre> 449 * Individual fields may be empty, but none of the three delimiting 450 * colons is ever omitted. If this instance uses pattern matching 451 * syntax to match multiple probes, that syntax is preserved in the 452 * string representation. 453 */ 454 public String 455 toString() 456 { 457 StringBuffer buf = new StringBuffer(); 458 buf.append(provider); 459 buf.append(':'); 460 buf.append(module); 461 buf.append(':'); 462 buf.append(function); 463 buf.append(':'); 464 buf.append(name); 465 return buf.toString(); 466 } 467 } 468