/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * ident "%Z%%M% %I% %E% SMI" */ package org.opensolaris.os.dtrace; import java.util.*; import java.text.ParseException; import java.io.*; import java.beans.*; /** * A DTrace probe description consists of provider, module, function, * and name. A single probe description may identify a single DTrace * probe or match multiple probes. Any field may be wildcarded by * omission (set to null) or set to a glob-style pattern: *
 *    *		Matches any string, including the null string
 *    ?		Matches any single character
 *    [ ... ]	Matches any one of the enclosed characters. A pair of
 *    			characters separated by - matches any character
 *    			between the pair, inclusive. If the first
 *    			character after the [ is !, any character not
 *    			enclosed in the set is matched.
 *    \		Interpret the next character as itself, without any
 *    			special meaning
 * 
* Immutable. Supports persistence using {@link java.beans.XMLEncoder}. * * @see Consumer#listProbes(ProbeDescription filter) * * @author Tom Erickson */ public final class ProbeDescription implements Serializable, Comparable { static final long serialVersionUID = 5978023304364513667L; /** * Instance with empty provider, module, function, and name fields * matches all DTrace probes on a system. */ public static final ProbeDescription EMPTY = new ProbeDescription(null, null, null, null); private static final int ID_NONE = -1; /** * Enumerates the provider, module, function, and name fields of a * probe description. */ public enum Spec { /** Probe provider */ PROVIDER, /** Probe module */ MODULE, /** Probe function */ FUNCTION, /** Probe name (unqualified) */ NAME }; static { try { BeanInfo info = Introspector.getBeanInfo(ProbeDescription.class); PersistenceDelegate persistenceDelegate = new DefaultPersistenceDelegate( new String[] {"ID", "provider", "module", "function", "name"}); BeanDescriptor d = info.getBeanDescriptor(); d.setValue("persistenceDelegate", persistenceDelegate); } catch (IntrospectionException e) { System.out.println(e); } } /** @serial */ private int id = ID_NONE; // set by native code /** @serial */ private final String provider; /** @serial */ private final String module; /** @serial */ private final String function; /** @serial */ private final String name; /** * Creates a fully qualified probe description from the name given * in the format {@code provider:module:function:name} or * else a probe description that specifies only the unqualified * probe name. * * @param probeName either the fully qualified name in the format * {@code provider:module:function:name} or else (if no colon * is present) the unqualified name interpreted as {@code * :::probeName} * @see ProbeDescription#ProbeDescription(String probeProvider, * String probeModule, String probeFunction, String probeName) * @see ProbeDescription#parse(String s) */ public ProbeDescription(String probeName) { if ((probeName != null) && (probeName.indexOf(':') >= 0)) { ProbeDescription p; try { p = ProbeDescription.parse(probeName); } catch (ParseException e) { p = null; } if (p == null) { provider = ""; module = ""; function = ""; name = ((probeName == null) ? "" : probeName); } else { provider = p.provider; module = p.module; function = p.function; name = p.name; } } else { provider = ""; module = ""; function = ""; name = ((probeName == null) ? "" : probeName); } } /** * Creates a probe description that specifies the probe name * qualified only by the function name. * * @see ProbeDescription#ProbeDescription(String probeProvider, * String probeModule, String probeFunction, String probeName) */ public ProbeDescription(String probeFunction, String probeName) { this(null, null, probeFunction, probeName); } /** * Creates a probe description that specifies the probe name * qualified by the function name and module name. * * @see ProbeDescription#ProbeDescription(String probeProvider, * String probeModule, String probeFunction, String probeName) */ public ProbeDescription(String probeModule, String probeFunction, String probeName) { this(null, probeModule, probeFunction, probeName); } /** * Creates a fully qualified probe description. If no pattern * syntax is used and no field is omitted, the resulting description * matches at most one DTrace probe. * * @param probeProvider provider name, may be null or empty to match * all providers or use pattern syntax to match multiple providers * @param probeModule module name, may be null or empty to match all * modules or use pattern syntax to match multiple modules * @param probeFunction function name, may be null or empty to match * all functions or use pattern syntax to match multiple functions * @param probeName unqualified probe name, may be null or empty to * match all names or use pattern syntax to match multiple names */ public ProbeDescription(String probeProvider, String probeModule, String probeFunction, String probeName) { provider = ((probeProvider == null) ? "" : probeProvider); module = ((probeModule == null) ? "" : probeModule); function = ((probeFunction == null) ? "" : probeFunction); name = ((probeName == null) ? "" : probeName); } /** * Supports XML persistence. */ public ProbeDescription(int probeID, String probeProvider, String probeModule, String probeFunction, String probeName) { this(probeProvider, probeModule, probeFunction, probeName); id = probeID; } /** * Generates a probe description from a string in the same format * returned by {@link #toString()}. Parses the string from right to * left. *

     * provider:module:function:name
     * 
* * @return non-null probe description * @throws ParseException if {@code s} does not have the expected * format. The error offset is the index of the first unexpected * character encountered starting from the last character and * reading backwards. * @throws NullPointerException if the given string is {@code null} */ public static ProbeDescription parse(String s) throws ParseException { ProbeDescription p; // StringTokenizer and String.split() do not correctly handle // the case of consecutive delimiters List list = new ArrayList (); int len = s.length(); int npos = len; char ch; for (int i = (len - 1); i >= 0; --i) { ch = s.charAt(i); if (ch == ':') { list.add(0, s.substring((i + 1), npos)); npos = i; } } list.add(0, s.substring(0, npos)); switch (list.size()) { case 0: p = EMPTY; break; case 1: p = new ProbeDescription(list.get(0)); break; case 2: p = new ProbeDescription(list.get(0), list.get(1)); break; case 3: p = new ProbeDescription(list.get(0), list.get(1), list.get(2)); break; case 4: p = new ProbeDescription(list.get(0), list.get(1), list.get(2), list.get(3)); break; default: // get error offset (parsing right-to-left) int offset = (s.length() - 4); len = list.size(); for (int i = (len - 1); i >= (len - 4); --i) { offset -= list.get(i).length(); } throw new ParseException("Overspecified probe " + "description: \"" + s + "\"", offset); } return p; } /** * Gets the probe ID. * * @return ID generated from a sequence by the native DTrace * library, identifies the probe among all probes on the system */ public int getID() { return id; } /** * Gets the provider name. * * @return non-null provider name, may be an empty string to * indicate omission */ public String getProvider() { return provider; } /** * Gets the module name. * * @return non-null module name, may be an empty string to indicate * omission */ public String getModule() { return module; } /** * Gets the function name. * * @return non-null function name, may be an empty string to * indicate omission */ public String getFunction() { return function; } /** * Gets the unqualified probe name. * * @return non-null probe name, may be an empty string to indicate * omission */ public String getName() { return name; } /** * Returns {@code true} if provider, module, function, and name are * all omitted. An empty probe description matches all DTrace * probes on a system. * * @return {@code true} if all probe fields are omitted, {@code * false} otherwise */ public boolean isEmpty() { if (provider.length() > 0) { return false; } if (module.length() > 0) { return false; } if (function.length() > 0) { return false; } if (name.length() > 0) { return false; } return true; } /** * Compares the specified object with this probe description for * equality. Defines equality as having the same fields. Omitted * fields must be omitted in both instances in order for them to be * equal, but it makes no difference whether {@code null} or empty * string was used to indicate omission. * * @return {@code true} if and only if all corresponding fields of * both probe descriptions are either both omitted (null or empty) * or else equal as defined by {@link String#equals(Object o) * String.equals()} */ public boolean equals(Object o) { if (o instanceof ProbeDescription) { ProbeDescription p = (ProbeDescription)o; if ((id == ID_NONE) || (p.id == ID_NONE)) { return (compareTo(p) == 0); } else { return (id == p.id); } } return false; } /** * Defines the natural ordering of probe descriptions. Returns the * natural ordering of the first unequal pair of corresponding * fields (starting with the provider and continuing to the * unqualified name only if all other fields are equal). * Corresponding fields are equal if they are both omitted or both * equal as defined by {@link String#equals(Object o) * String.equals()}. It makes no difference if {@code null} or * empty string is used to indicate omission. The behavior is * consistent with the {@link #equals(Object o) equals()} method. * * @return -1, 0, or 1 as this probe description is less than, equal * to, or greater than the given probe description */ public int compareTo(ProbeDescription p) { int cmp = 0; cmp = provider.compareTo(p.provider); if (cmp == 0) { cmp = module.compareTo(p.module); if (cmp == 0) { cmp = function.compareTo(p.function); if (cmp == 0) { cmp = name.compareTo(p.name); } } } return (cmp); } /** * Overridden to ensure that equal probe descriptions have equal * hashcodes. */ @Override public int hashCode() { int hash = id; if (hash != ID_NONE) { return hash; } hash = 17; hash = (37 * hash) + provider.hashCode(); hash = (37 * hash) + module.hashCode(); hash = (37 * hash) + function.hashCode(); hash = (37 * hash) + name.hashCode(); return hash; } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); // check invariants if (provider == null) { throw new InvalidObjectException("provider is null"); } if (module == null) { throw new InvalidObjectException("module is null"); } if (function == null) { throw new InvalidObjectException("function is null"); } if (name == null) { throw new InvalidObjectException("name is null"); } } /** * Gets the string representation of this probe description. The * format is as follows: *

     * provider:module:function:name
     * 
* Individual fields may be empty, but none of the three delimiting * colons is ever omitted. If this instance uses pattern matching * syntax to match multiple probes, that syntax is preserved in the * string representation. */ public String toString() { StringBuilder buf = new StringBuilder(); buf.append(provider); buf.append(':'); buf.append(module); buf.append(':'); buf.append(function); buf.append(':'); buf.append(name); return buf.toString(); } }