xref: /illumos-gate/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/ProbeDescription.java (revision 2caf0dcd2abc26b477e317999994020212790d38)
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