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.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 fully qualified probe description from the name given 114 * in the format <i>{@code provider:module:function:name}</i> or 115 * else a probe description that specifies only the unqualified 116 * probe name. 117 * 118 * @param probeName either the fully qualified name in the format 119 * <i>{@code provider:module:function:name}</i> or else (if no colon 120 * is present) the unqualified name interpreted as {@code 121 * :::probeName} 122 * @see ProbeDescription#ProbeDescription(String probeProvider, 123 * String probeModule, String probeFunction, String probeName) 124 * @see ProbeDescription#parse(String s) 125 */ 126 public ProbeDescription(String probeName)127 ProbeDescription(String probeName) 128 { 129 if ((probeName != null) && (probeName.indexOf(':') >= 0)) { 130 ProbeDescription p; 131 try { 132 p = ProbeDescription.parse(probeName); 133 } catch (ParseException e) { 134 p = null; 135 } 136 137 if (p == null) { 138 provider = ""; 139 module = ""; 140 function = ""; 141 name = ((probeName == null) ? "" : probeName); 142 } else { 143 provider = p.provider; 144 module = p.module; 145 function = p.function; 146 name = p.name; 147 } 148 } else { 149 provider = ""; 150 module = ""; 151 function = ""; 152 name = ((probeName == null) ? "" : probeName); 153 } 154 } 155 156 /** 157 * Creates a probe description that specifies the probe name 158 * qualified only by the function name. 159 * 160 * @see ProbeDescription#ProbeDescription(String probeProvider, 161 * String probeModule, String probeFunction, String probeName) 162 */ 163 public ProbeDescription(String probeFunction, String probeName)164 ProbeDescription(String probeFunction, String probeName) 165 { 166 this(null, null, probeFunction, probeName); 167 } 168 169 /** 170 * Creates a probe description that specifies the probe name 171 * qualified by the function name and module name. 172 * 173 * @see ProbeDescription#ProbeDescription(String probeProvider, 174 * String probeModule, String probeFunction, String probeName) 175 */ 176 public ProbeDescription(String probeModule, String probeFunction, String probeName)177 ProbeDescription(String probeModule, String probeFunction, 178 String probeName) 179 { 180 this(null, probeModule, probeFunction, probeName); 181 } 182 183 /** 184 * Creates a fully qualified probe description. If no pattern 185 * syntax is used and no field is omitted, the resulting description 186 * matches at most one DTrace probe. 187 * 188 * @param probeProvider provider name, may be null or empty to match 189 * all providers or use pattern syntax to match multiple providers 190 * @param probeModule module name, may be null or empty to match all 191 * modules or use pattern syntax to match multiple modules 192 * @param probeFunction function name, may be null or empty to match 193 * all functions or use pattern syntax to match multiple functions 194 * @param probeName unqualified probe name, may be null or empty to 195 * match all names or use pattern syntax to match multiple names 196 */ 197 public ProbeDescription(String probeProvider, String probeModule, String probeFunction, String probeName)198 ProbeDescription(String probeProvider, 199 String probeModule, 200 String probeFunction, 201 String probeName) 202 { 203 provider = ((probeProvider == null) ? "" : probeProvider); 204 module = ((probeModule == null) ? "" : probeModule); 205 function = ((probeFunction == null) ? "" : probeFunction); 206 name = ((probeName == null) ? "" : probeName); 207 } 208 209 /** 210 * Supports XML persistence. 211 */ 212 public ProbeDescription(int probeID, String probeProvider, String probeModule, String probeFunction, String probeName)213 ProbeDescription(int probeID, 214 String probeProvider, 215 String probeModule, 216 String probeFunction, 217 String probeName) 218 { 219 this(probeProvider, probeModule, probeFunction, probeName); 220 id = probeID; 221 } 222 223 /** 224 * Generates a probe description from a string in the same format 225 * returned by {@link #toString()}. Parses the string from right to 226 * left. 227 * <pre><code> 228 * <i>provider:module:function:name</i> 229 * </code></pre> 230 * 231 * @return non-null probe description 232 * @throws ParseException if {@code s} does not have the expected 233 * format. The error offset is the index of the first unexpected 234 * character encountered starting from the last character and 235 * reading backwards. 236 * @throws NullPointerException if the given string is {@code null} 237 */ 238 public static ProbeDescription parse(String s)239 parse(String s) throws ParseException 240 { 241 ProbeDescription p; 242 243 // StringTokenizer and String.split() do not correctly handle 244 // the case of consecutive delimiters 245 List <String> list = new ArrayList <String> (); 246 int len = s.length(); 247 int npos = len; 248 char ch; 249 for (int i = (len - 1); i >= 0; --i) { 250 ch = s.charAt(i); 251 if (ch == ':') { 252 list.add(0, s.substring((i + 1), npos)); 253 npos = i; 254 } 255 } 256 list.add(0, s.substring(0, npos)); 257 258 switch (list.size()) { 259 case 0: 260 p = EMPTY; 261 break; 262 case 1: 263 p = new ProbeDescription(list.get(0)); 264 break; 265 case 2: 266 p = new ProbeDescription(list.get(0), list.get(1)); 267 break; 268 case 3: 269 p = new ProbeDescription(list.get(0), list.get(1), 270 list.get(2)); 271 break; 272 case 4: 273 p = new ProbeDescription(list.get(0), list.get(1), 274 list.get(2), list.get(3)); 275 break; 276 default: 277 // get error offset (parsing right-to-left) 278 int offset = (s.length() - 4); 279 len = list.size(); 280 for (int i = (len - 1); i >= (len - 4); --i) { 281 offset -= list.get(i).length(); 282 } 283 throw new ParseException("Overspecified probe " + 284 "description: \"" + s + "\"", offset); 285 } 286 return p; 287 } 288 289 /** 290 * Gets the probe ID. 291 * 292 * @return ID generated from a sequence by the native DTrace 293 * library, identifies the probe among all probes on the system 294 */ 295 public int getID()296 getID() 297 { 298 return id; 299 } 300 301 /** 302 * Gets the provider name. 303 * 304 * @return non-null provider name, may be an empty string to 305 * indicate omission 306 */ 307 public String getProvider()308 getProvider() 309 { 310 return provider; 311 } 312 313 /** 314 * Gets the module name. 315 * 316 * @return non-null module name, may be an empty string to indicate 317 * omission 318 */ 319 public String getModule()320 getModule() 321 { 322 return module; 323 } 324 325 /** 326 * Gets the function name. 327 * 328 * @return non-null function name, may be an empty string to 329 * indicate omission 330 */ 331 public String getFunction()332 getFunction() 333 { 334 return function; 335 } 336 337 /** 338 * Gets the unqualified probe name. 339 * 340 * @return non-null probe name, may be an empty string to indicate 341 * omission 342 */ 343 public String getName()344 getName() 345 { 346 return name; 347 } 348 349 /** 350 * Returns {@code true} if provider, module, function, and name are 351 * all omitted. An empty probe description matches all DTrace 352 * probes on a system. 353 * 354 * @return {@code true} if all probe fields are omitted, {@code 355 * false} otherwise 356 */ 357 public boolean isEmpty()358 isEmpty() 359 { 360 if (provider.length() > 0) { 361 return false; 362 } 363 if (module.length() > 0) { 364 return false; 365 } 366 if (function.length() > 0) { 367 return false; 368 } 369 if (name.length() > 0) { 370 return false; 371 } 372 return true; 373 } 374 375 /** 376 * Compares the specified object with this probe description for 377 * equality. Defines equality as having the same fields. Omitted 378 * fields must be omitted in both instances in order for them to be 379 * equal, but it makes no difference whether {@code null} or empty 380 * string was used to indicate omission. 381 * 382 * @return {@code true} if and only if all corresponding fields of 383 * both probe descriptions are either both omitted (null or empty) 384 * or else equal as defined by {@link String#equals(Object o) 385 * String.equals()} 386 */ 387 public boolean equals(Object o)388 equals(Object o) 389 { 390 if (o instanceof ProbeDescription) { 391 ProbeDescription p = (ProbeDescription)o; 392 if ((id == ID_NONE) || (p.id == ID_NONE)) { 393 return (compareTo(p) == 0); 394 } else { 395 return (id == p.id); 396 } 397 } 398 399 return false; 400 } 401 402 /** 403 * Defines the natural ordering of probe descriptions. Returns the 404 * natural ordering of the first unequal pair of corresponding 405 * fields (starting with the provider and continuing to the 406 * unqualified name only if all other fields are equal). 407 * Corresponding fields are equal if they are both omitted or both 408 * equal as defined by {@link String#equals(Object o) 409 * String.equals()}. It makes no difference if {@code null} or 410 * empty string is used to indicate omission. The behavior is 411 * consistent with the {@link #equals(Object o) equals()} method. 412 * 413 * @return -1, 0, or 1 as this probe description is less than, equal 414 * to, or greater than the given probe description 415 */ 416 public int compareTo(ProbeDescription p)417 compareTo(ProbeDescription p) 418 { 419 int cmp = 0; 420 cmp = provider.compareTo(p.provider); 421 if (cmp == 0) { 422 cmp = module.compareTo(p.module); 423 if (cmp == 0) { 424 cmp = function.compareTo(p.function); 425 if (cmp == 0) { 426 cmp = name.compareTo(p.name); 427 } 428 } 429 } 430 return (cmp); 431 } 432 433 /** 434 * Overridden to ensure that equal probe descriptions have equal 435 * hashcodes. 436 */ 437 @Override 438 public int hashCode()439 hashCode() 440 { 441 int hash = id; 442 if (hash != ID_NONE) { 443 return hash; 444 } 445 446 hash = 17; 447 hash = (37 * hash) + provider.hashCode(); 448 hash = (37 * hash) + module.hashCode(); 449 hash = (37 * hash) + function.hashCode(); 450 hash = (37 * hash) + name.hashCode(); 451 return hash; 452 } 453 454 private void readObject(ObjectInputStream s)455 readObject(ObjectInputStream s) 456 throws IOException, ClassNotFoundException 457 { 458 s.defaultReadObject(); 459 // check invariants 460 if (provider == null) { 461 throw new InvalidObjectException("provider is null"); 462 } 463 if (module == null) { 464 throw new InvalidObjectException("module is null"); 465 } 466 if (function == null) { 467 throw new InvalidObjectException("function is null"); 468 } 469 if (name == null) { 470 throw new InvalidObjectException("name is null"); 471 } 472 } 473 474 /** 475 * Gets the string representation of this probe description. The 476 * format is as follows: 477 * <pre><code> 478 * <i>provider:module:function:name</i> 479 * </code></pre> 480 * Individual fields may be empty, but none of the three delimiting 481 * colons is ever omitted. If this instance uses pattern matching 482 * syntax to match multiple probes, that syntax is preserved in the 483 * string representation. 484 */ 485 public String toString()486 toString() 487 { 488 StringBuilder buf = new StringBuilder(); 489 buf.append(provider); 490 buf.append(':'); 491 buf.append(module); 492 buf.append(':'); 493 buf.append(function); 494 buf.append(':'); 495 buf.append(name); 496 return buf.toString(); 497 } 498 } 499