xref: /titanic_52/usr/src/cmd/dtrace/test/cmd/jdtrace/JDTrace.java (revision d2ec54f7875f7e05edd56195adbeb593c947763f)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * ident	"%Z%%M%	%I%	%E% SMI"
27  */
28 import org.opensolaris.os.dtrace.*;
29 import java.io.*;
30 import java.util.*;
31 import java.util.logging.*;
32 
33 /**
34  * Emulates {@code dtrace(1M)} using the Java DTrace API.
35  */
36 public class JDTrace {
37     static Logger logger = Logger.getLogger(JDTrace.class.getName());
38 
39     static Consumer dtrace;
40 
41     static {
42 	Handler handler = new ConsoleHandler();
43 	handler.setLevel(Level.ALL);
44 	logger.addHandler(handler);
45     }
46 
47     static final String CLASSNAME = "JDTrace";
48     static final String OPTSTR =
49 	    "3:6:b:c:CD:ef:Fi:I:lL:m:n:o:p:P:qs:U:vVwx:X:Z";
50     static boolean heading = false;
51     static boolean quiet = false;
52     static boolean flow = false;
53     static int stackindent = 14;
54     static int exitStatus = 0;
55     static boolean started;
56     static boolean stopped;
57     static PrintStream out = System.out;
58     static final String ATS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
59     static final String SPACES = "                                        ";
60     static final int QUANTIZE_ZERO_BUCKET = 63;
61 
62     enum Mode {
63 	EXEC,
64 	INFO,
65 	LIST,
66 	VERSION
67     }
68 
69     enum ProgramType {
70 	STRING,
71 	FILE
72     }
73 
74     static class CompileRequest {
75 	String s;
76 	ProgramType type;
77 	ProbeDescription.Spec probespec;
78     }
79 
80     // Modify program string by expanding an incomplete probe
81     // description according to the requested probespec.
82     static void
83     applyProbespec(CompileRequest req)
84     {
85 	ProbeDescription.Spec spec = ((req.probespec == null)
86 		? ProbeDescription.Spec.NAME
87 		: req.probespec);
88 
89 	int colons = 0;
90 	switch (req.probespec) {
91 	    case PROVIDER:
92 		colons = 3;
93 		break;
94 	    case MODULE:
95 		colons = 2;
96 		break;
97 	    case FUNCTION:
98 		colons = 1;
99 		break;
100 	}
101 
102 	StringBuffer buf = new StringBuffer();
103 	if (colons > 0) {
104 	    char ch;
105 	    int len = req.s.length();
106 
107 	    int i = 0;
108 	    // Find first whitespace character not including leading
109 	    // whitespace (end of first token).  Ignore whitespace
110 	    // inside a block if the block is concatenated with the
111 	    // probe description.
112 	    for (; (i < len) && Character.isWhitespace(req.s.charAt(i)); ++i);
113 	    int npos = i;
114 	    boolean inBlock = false;
115 	    for (; (npos < len) &&
116 		    (!Character.isWhitespace(ch = req.s.charAt(npos)) ||
117 		    inBlock); ++npos) {
118 		if (ch == '{') {
119 		    inBlock = true;
120 		} else if (ch == '}') {
121 		    inBlock = false;
122 		}
123 	    }
124 
125 	    // libdtrace lets you concatenate multiple probe
126 	    // descriptions separated by code blocks in curly braces,
127 	    // for example genunix::'{printf("FOUND");}'::entry, as long
128 	    // as the concatenated probe descriptions begin with ':' and
129 	    // not a specific field such as 'syscall'.  So to expand the
130 	    // possibly multiple probe descriptions, we need to insert
131 	    // colons before each open curly brace, and again at the end
132 	    // only if there is at least one non-whitespace (probe
133 	    // specifying) character after the last closing curly brace.
134 
135 	    int prev_i = 0;
136 	    while (i < npos) {
137 		for (; (i < npos) && (req.s.charAt(i) != '{'); ++i);
138 		buf.append(req.s.substring(prev_i, i));
139 		if ((i < npos) || ((i > 0) && (req.s.charAt(i - 1) != '}'))) {
140 		    for (int c = 0; c < colons; ++c) {
141 			buf.append(':');
142 		    }
143 		}
144 		if (i < npos) {
145 		    buf.append(req.s.charAt(i++));
146 		}
147 		prev_i = i;
148 	    }
149 
150 	    // append remainder of program text
151 	    buf.append(req.s.substring(i));
152 
153 	    req.s = buf.toString();
154 	}
155     }
156 
157     static void
158     printValue(Object value, int bytes, String stringFormat)
159     {
160 	if (value instanceof Integer) {
161 	    if (bytes == 1) {
162 		out.printf(" %3d", (Integer)value);
163 	    } else if (bytes == 2) {
164 		out.printf(" %5d", (Integer)value);
165 	    } else {
166 		out.printf(" %8d", (Integer)value);
167 	    }
168 	} else if (value instanceof Long) {
169 	    out.printf(" %16d", (Long)value);
170 	} else {
171 	    out.printf(stringFormat, value.toString());
172 	}
173     }
174 
175     static void
176     consumeProbeData(ProbeData data)
177     {
178 	if (logger.isLoggable(Level.FINER)) {
179 	    logger.finer(data.toString());
180 	}
181 
182 	if (!heading) {
183 	    if (flow) {
184 		out.printf("%3s %-41s\n", "CPU", "FUNCTION");
185 	    } else {
186 		if (!quiet) {
187 		    out.printf("%3s %6s %32s\n",
188 			    "CPU", "ID", "FUNCTION:NAME");
189 		}
190 	    }
191 	    heading = true;
192 	}
193 	ProbeDescription probe = data.getEnabledProbeDescription();
194 	if (flow) {
195 	    Flow flow = data.getFlow();
196 	    int indent = (flow.getDepth() * 2);
197 	    StringBuffer buf = new StringBuffer();
198 	    // indent
199 	    buf.append(' ');
200 	    for (int i = 0; i < indent; ++i) {
201 		buf.append(' ');
202 	    }
203 	    // prefix
204 	    switch (flow.getKind()) {
205 		case ENTRY:
206 		    if (indent == 0) {
207 			buf.append("=> ");
208 		    } else {
209 			buf.append("-> ");
210 		    }
211 		    break;
212 		case RETURN:
213 		    if (indent == 0) {
214 			buf.append("<= ");
215 		    } else {
216 			buf.append("<- ");
217 		    }
218 		    break;
219 	    }
220 
221 	    switch (flow.getKind()) {
222 		case NONE:
223 		    buf.append(probe.getFunction());
224 		    buf.append(':');
225 		    buf.append(probe.getName());
226 		    break;
227 		default:
228 		    buf.append(probe.getFunction());
229 	    }
230 
231 	    out.printf("%3s %-41s ", data.getCPU(),
232 		    buf.toString());
233 	} else {
234 	    if (!quiet) {
235 		StringBuffer buf = new StringBuffer();
236 		buf.append(probe.getFunction());
237 		buf.append(':');
238 		buf.append(probe.getName());
239 		out.printf("%3s %6s %32s ",
240 			data.getCPU(), probe.getID(),
241 			buf.toString());
242 	    }
243 	}
244 	Record record = null;
245 	Object value;
246 	List <Record> records = data.getRecords();
247 	Iterator <Record> itr = records.iterator();
248 	while (itr.hasNext()) {
249 	    record = itr.next();
250 
251 	    if (record instanceof ExitRecord) {
252 		exitStatus = ((ExitRecord)record).getStatus();
253 	    } else if (record instanceof ScalarRecord) {
254 		ScalarRecord scalar = (ScalarRecord)record;
255 		value = scalar.getValue();
256 		if (value instanceof byte[]) {
257 		    out.print(record.toString());
258 		} else {
259 		    if (quiet) {
260 			out.print(value);
261 		    } else {
262 			printValue(value, scalar.getNumberOfBytes(),
263 				"  %-33s");
264 		    }
265 		}
266 	    } else if (record instanceof PrintfRecord) {
267 		out.print(record);
268 	    } else if (record instanceof PrintaRecord) {
269 		PrintaRecord printa = (PrintaRecord)record;
270 		List <Tuple> tuples = printa.getTuples();
271 		if (tuples.isEmpty()) {
272 		    out.print(printa.getOutput());
273 		} else {
274 		    for (Tuple t : tuples) {
275 			out.print(printa.getFormattedString(t));
276 		    }
277 		}
278 
279 		if (logger.isLoggable(Level.FINE)) {
280 		    logger.fine(printa.toString());
281 		}
282 	    } else if (record instanceof StackValueRecord) {
283 		printStack((StackValueRecord)record);
284 	    }
285 	}
286 	if (!quiet) {
287 	    out.println();
288 	}
289     }
290 
291     static void
292     printDistribution(Distribution d)
293     {
294 	out.printf("\n%16s %41s %-9s\n", "value",
295 		"------------- Distribution -------------",
296 		"count");
297 	long v; // bucket frequency (value)
298 	long b; // lower bound of bucket range
299 	double total = 0;
300 	boolean positives = false;
301 	boolean negatives = false;
302 
303 	Distribution.Bucket bucket;
304 	int b1 = 0; // first displayed bucket
305 	int b2 = d.size() - 1; // last displayed bucket
306 	for (; (b1 <= b2) && (d.get(b1).getFrequency() == 0); ++b1);
307 	// If possible, get one bucket before the first non-zero
308 	// bucket and one bucket after the last.
309 	if (b1 > b2) {
310 	    // There isn't any data.  This is possible if (and only if)
311 	    // negative increment values have been used.  In this case,
312 	    // print the buckets around the base.
313 	    if (d instanceof LinearDistribution) {
314 		b1 = 0;
315 		b2 = 2;
316 	    } else {
317 		b1 = QUANTIZE_ZERO_BUCKET - 1;
318 		b2 = QUANTIZE_ZERO_BUCKET + 1;
319 	    }
320 	} else {
321 	    if (b1 > 0) --b1;
322 	    for (; (b2 > 0) && (d.get(b2).getFrequency() == 0); --b2);
323 	    if (b2 < (d.size() - 1)) ++b2;
324 	}
325 	for (int i = b1; i <= b2; ++i) {
326 	    v = d.get(i).getFrequency();
327 	    if (v > 0) {
328 		positives = true;
329 	    }
330 	    if (v < 0) {
331 		negatives = true;
332 	    }
333 	    total += Math.abs((double)v);
334 	}
335 	for (int i = b1; i <= b2; ++i) {
336 	    bucket = d.get(i);
337 	    v = bucket.getFrequency();
338 	    b = bucket.getMin();
339 
340 	    if (d instanceof LinearDistribution) {
341 		if (b == Long.MIN_VALUE) {
342 		    String lt = "< " + ((LinearDistribution)d).getBase();
343 		    out.printf("%16s ", lt);
344 		} else if (bucket.getMax() == Long.MAX_VALUE) {
345 		    String ge = ">= " + b;
346 		    out.printf("%16s ", ge);
347 		} else {
348 		    out.printf("%16d ", b);
349 		}
350 	    } else {
351 		out.printf("%16d ", b);
352 	    }
353 
354 	    printDistributionLine(v, total, positives, negatives);
355 	}
356     }
357 
358     static void
359     printDistributionLine(long val, double total, boolean positives,
360 	    boolean negatives)
361     {
362 	double f;
363 	int depth, len = 40;
364 
365 	assert (ATS.length() == len && SPACES.length() == len);
366 	assert (!(total == 0 && (positives || negatives)));
367 	assert (!(val < 0 && !negatives));
368 	assert (!(val > 0 && !positives));
369 	assert (!(val != 0 && total == 0));
370 
371 	if (!negatives) {
372 	    if (positives) {
373 		f = (Math.abs((double)val) * (double)len) / total;
374 		    depth = (int)(f + 0.5);
375 	    } else {
376 		depth = 0;
377 	    }
378 
379 	    out.printf("|%s%s %-9d\n", ATS.substring(len - depth),
380 		    SPACES.substring(depth), val);
381 	    return;
382 	}
383 
384 	if (!positives) {
385 	    f = (Math.abs((double)val) * (double)len) / total;
386 	    depth = (int)(f + 0.5);
387 
388 	    out.printf("%s%s| %-9d\n", SPACES.substring(depth),
389 		    ATS.substring(len - depth), val);
390 	    return;
391 	}
392 
393 	/*
394 	 * If we're here, we have both positive and negative bucket values.
395 	 * To express this graphically, we're going to generate both positive
396 	 * and negative bars separated by a centerline.  These bars are half
397 	 * the size of normal quantize()/lquantize() bars, so we divide the
398 	 * length in half before calculating the bar length.
399 	 */
400 	len /= 2;
401 	String ats = ATS.substring(len);
402 	String spaces = SPACES.substring(len);
403 
404 	f = (Math.abs((double)val) * (double)len) / total;
405 	depth = (int)(f + 0.5);
406 
407 	if (val <= 0) {
408 	    out.printf("%s%s|%s %-9d\n", spaces.substring(depth),
409 		    ats.substring(len - depth), repeat(" ", len), val);
410 	    return;
411 	} else {
412 	    out.printf("%20s|%s%s %-9d\n", "", ats.substring(len - depth),
413 		    spaces.substring(depth), val);
414 	}
415     }
416 
417     public static String
418     repeat(String s, int n)
419     {
420         StringBuffer buf = new StringBuffer();
421         for (int i = 0; i < n; ++i) {
422             buf.append(s);
423         }
424         return buf.toString();
425     }
426 
427     static void
428     printStack(StackValueRecord rec)
429     {
430 	StackFrame[] frames = rec.getStackFrames();
431 	int i;
432 	out.println();
433 	String s;
434 	for (StackFrame f : frames) {
435 	    for (i = 0; i < stackindent; ++i) {
436 		out.print(' ');
437 	    }
438 	    s = f.getFrame();
439 	    if (s.indexOf('[') == 0) {
440 		out.print("  ");
441 	    }
442 	    out.println(s);
443 	}
444     }
445 
446     static void
447     printAggregate(Aggregate aggregate)
448     {
449 	printAggregationRecords(aggregate.getOrderedRecords());
450     }
451 
452     static void
453     printAggregationRecords(List <AggregationRecord> list)
454     {
455 	Tuple tuple;
456 	AggregationValue value;
457 	ValueRecord tupleRecord;
458 	int i;
459 	int len;
460 	for (AggregationRecord r : list) {
461 	    tuple = r.getTuple();
462 	    value = r.getValue();
463 	    len = tuple.size();
464 	    for (i = 0; i < len; ++i) {
465 		tupleRecord = tuple.get(i);
466 		if (tupleRecord instanceof StackValueRecord) {
467 		    printStack((StackValueRecord)tupleRecord);
468 		} else if (tupleRecord instanceof SymbolValueRecord) {
469 		    printValue(tupleRecord.toString(), -1, "  %-50s");
470 		} else {
471 		    printValue(tupleRecord.getValue(),
472 			    ((ScalarRecord)tupleRecord).getNumberOfBytes(),
473 			    "  %-50s");
474 		}
475 	    }
476 	    if (value instanceof Distribution) {
477 		Distribution d = (Distribution)value;
478 		printDistribution(d);
479 	    } else {
480 		Number v = value.getValue();
481 		printValue(v, -1, "  %-50s");
482 	    }
483 	    out.println();
484 	}
485     }
486 
487     static void
488     exit(int status)
489     {
490 	out.flush();
491 	System.err.flush();
492 	if (status == 0) {
493 	    status = exitStatus;
494 	}
495 	System.exit(status);
496     }
497 
498     static void
499     usage()
500     {
501 	String predact = "[[ predicate ] action ]";
502 	System.err.printf("Usage: java %s [-32|-64] [-CeFlqvVwZ] " +
503 	    "[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] " +
504 	    "[-o output] [-p pid] [-s script] [-U name]\n\t" +
505 	    "[-x opt[=val]] [-X a|c|s|t]\n\n" +
506 	    "\t[-P provider %s]\n" +
507 	    "\t[-m [ provider: ] module %s]\n" +
508 	    "\t[-f [[ provider: ] module: ] func %s]\n" +
509 	    "\t[-n [[[ provider: ] module: ] func: ] name %s]\n" +
510 	    "\t[-i probe-id %s] [ args ... ]\n\n", CLASSNAME,
511 	    predact, predact, predact, predact, predact);
512 	System.err.printf("\tpredicate -> '/' D-expression '/'\n");
513 	System.err.printf("\t   action -> '{' D-statements '}'\n");
514 	System.err.printf("\n" +
515 	    "\t-32 generate 32-bit D programs\n" +
516 	    "\t-64 generate 64-bit D programs\n\n" +
517 	    "\t-b  set trace buffer size\n" +
518 	    "\t-c  run specified command and exit upon its completion\n" +
519 	    "\t-C  run cpp(1) preprocessor on script files\n" +
520 	    "\t-D  define symbol when invoking preprocessor\n" +
521 	    "\t-e  exit after compiling request but prior to enabling " +
522 		    "probes\n" +
523 	    "\t-f  enable or list probes matching the specified " +
524 		    "function name\n" +
525 	    "\t-F  coalesce trace output by function\n" +
526 	    "\t-i  enable or list probes matching the specified probe id\n" +
527 	    "\t-I  add include directory to preprocessor search path\n" +
528 	    "\t-l  list probes matching specified criteria\n" +
529 	    "\t-L  add library directory to library search path\n" +
530 	    "\t-m  enable or list probes matching the specified " +
531 		    "module name\n" +
532 	    "\t-n  enable or list probes matching the specified probe name\n" +
533 	    "\t-o  set output file\n" +
534 	    "\t-p  grab specified process-ID and cache its symbol tables\n" +
535 	    "\t-P  enable or list probes matching the specified " +
536 		    "provider name\n" +
537 	    "\t-q  set quiet mode (only output explicitly traced data)\n" +
538 	    "\t-s  enable or list probes according to the specified " +
539 		    "D script\n" +
540 	    "\t-U  undefine symbol when invoking preprocessor\n" +
541 	    "\t-v  set verbose mode (report stability attributes, " +
542 		    "arguments)\n" +
543 	    "\t-V  report DTrace API version\n" +
544 	    "\t-w  permit destructive actions\n" +
545 	    "\t-x  enable or modify compiler and tracing options\n" +
546 	    "\t-X  specify ISO C conformance settings for preprocessor\n" +
547 	    "\t-Z  permit probe descriptions that match zero probes\n" +
548 	    "\n" +
549 	    "\tTo log PrintaRecord, set this environment variable:\n" +
550 	    "\t\tJDTRACE_LOGGING_LEVEL=FINE\n" +
551 	    "\tTo log ProbeData, set JDTRACE_LOGGING_LEVEL=FINER\n");
552 	exit(2);
553     }
554 
555     static void
556     printProgramStability(String programType, String programDescription,
557 	    ProgramInfo info)
558     {
559 	out.println();
560 	out.printf("Stability data for %s %s:\n\n",
561 		programType, programDescription);
562 	InterfaceAttributes a;
563 	out.println("\tMinimum probe description " +
564 		"attributes");
565 	a = info.getMinimumProbeAttributes();
566 	out.printf("\t\tIdentifier Names: %s\n",
567 		a.getNameStability());
568 	out.printf("\t\tData Semantics:   %s\n",
569 		a.getDataStability());
570 	out.printf("\t\tDependency Class: %s\n",
571 		a.getDependencyClass());
572 	out.println("\tMinimum probe statement attributes");
573 	a = info.getMinimumStatementAttributes();
574 	out.printf("\t\tIdentifier Names: %s\n",
575 		a.getNameStability());
576 	out.printf("\t\tData Semantics:   %s\n",
577 		a.getDataStability());
578 	out.printf("\t\tDependency Class: %s\n",
579 		a.getDependencyClass());
580     }
581 
582     static void
583     printProbeDescription(ProbeDescription p)
584     {
585 	out.printf("%5d %10s %17s %33s %s\n", p.getID(),
586 	    p.getProvider(), p.getModule(),
587 	    p.getFunction(), p.getName());
588     }
589 
590     static void
591     printProbeInfo(ProbeInfo p)
592     {
593 	InterfaceAttributes a;
594 	out.println("\n\tProbe Description Attributes");
595 
596 	a = p.getProbeAttributes();
597 	out.printf("\t\tIdentifier Names: %s\n",
598 	    a.getNameStability());
599 	out.printf("\t\tData Semantics:   %s\n",
600 	    a.getDataStability());
601 	out.printf("\t\tDependency Class: %s\n",
602 	    a.getDependencyClass());
603 
604 	out.println("\n\tArgument Attributes");
605 
606 	a = p.getArgumentAttributes();
607 	out.printf("\t\tIdentifier Names: %s\n",
608 	    a.getNameStability());
609 	out.printf("\t\tData Semantics:   %s\n",
610 	    a.getDataStability());
611 	out.printf("\t\tDependency Class: %s\n",
612 	    a.getDependencyClass());
613 
614 	// Argument types unsupported for now.
615 
616 	out.println();
617     }
618 
619     public static void
620     main(String[] args)
621     {
622 	String loggingLevel = System.getenv().get("JDTRACE_LOGGING_LEVEL");
623 	try {
624 	    logger.setLevel(Level.parse(loggingLevel));
625 	} catch (Exception e) {
626 	    logger.setLevel(Level.OFF);
627 	}
628 
629 	if (args.length == 0) {
630 	    usage();
631 	}
632 
633 	List <CompileRequest> compileRequests = new LinkedList
634 		<CompileRequest> ();
635 	List <Program> programList = new LinkedList <Program> ();
636 	boolean verbose = false;
637 	Mode mode = Mode.EXEC;
638 
639 	final ExceptionHandler exceptionHandler = new ExceptionHandler() {
640 	    public void handleException(Throwable e) {
641 		if (e instanceof DTraceException) {
642 		    DTraceException de = (DTraceException)e;
643 		    System.err.printf("dtrace: %s\n", de.getMessage());
644 		} else if (e instanceof ConsumerException) {
645 		    ConsumerException ce = (ConsumerException)e;
646 		    Object msg = ce.getNotificationObject();
647 		    if ((msg instanceof org.opensolaris.os.dtrace.Error) ||
648 			(msg instanceof Drop)) {
649 			System.err.printf("dtrace: %s\n", ce.getMessage());
650 		    } else {
651 			ce.printStackTrace();
652 		    }
653 		} else {
654 		    e.printStackTrace();
655 		}
656 		exit(1);
657 	    }
658 	};
659 
660 	Getopt g = new Getopt(CLASSNAME, args, OPTSTR);
661 	int c = 0;
662 
663 	List <Consumer.OpenFlag> openFlags =
664 		new ArrayList <Consumer.OpenFlag> ();
665 
666 	while ((c = g.getopt()) != -1) {
667 	    switch (c) {
668 		case '3': {
669 		    String s = g.getOptarg();
670 		    if (!s.equals("2")) {
671 			System.err.println("dtrace: illegal option -- 3" + s);
672 			usage();
673 		    }
674 		    openFlags.add(Consumer.OpenFlag.ILP32);
675 		    break;
676 		}
677 		case '6': {
678 		    String s = g.getOptarg();
679 		    if (!s.equals("4")) {
680 			System.err.println("dtrace: illegal option -- 6" + s);
681 			usage();
682 		    }
683 		    openFlags.add(Consumer.OpenFlag.LP64);
684 		    break;
685 		}
686 	    }
687 	}
688 
689 	Consumer.OpenFlag[] oflags = new Consumer.OpenFlag[openFlags.size()];
690 	oflags = openFlags.toArray(oflags);
691 
692 	dtrace = new LocalConsumer() {
693 	    protected Thread createThread() {
694 		Thread t = super.createThread();
695 		t.setDaemon(false);
696 		t.setPriority(Thread.MIN_PRIORITY);
697 		return t;
698 	    }
699 	};
700 
701 	g = new Getopt(CLASSNAME, args, OPTSTR);
702 	c = 0;
703 
704 	try {
705 	    dtrace.open(oflags);
706 
707 	    // Set default options that may be overriden by options or #pragma
708 	    dtrace.setOption(Option.bufsize, Option.mb(4));
709 	    dtrace.setOption(Option.aggsize, Option.mb(4));
710 
711 	    CompileRequest r;
712 	    while ((c = g.getopt()) != -1) {
713 		switch (c) {
714 		    case 'b':
715 			dtrace.setOption(Option.bufsize, g.getOptarg());
716 			break;
717 		    case 'c':
718 			dtrace.createProcess(g.getOptarg());
719 			break;
720 		    case 'C':
721 			dtrace.setOption(Option.cpp);
722 			break;
723 		    case 'D':
724 			dtrace.setOption(Option.define, g.getOptarg());
725 			break;
726 		    case 'e':
727 			mode = Mode.INFO;
728 			break;
729 		    case 'f':
730 			r = new CompileRequest();
731 			r.s = g.getOptarg();
732 			r.type = ProgramType.STRING;
733 			r.probespec = ProbeDescription.Spec.FUNCTION;
734 			compileRequests.add(r);
735 			break;
736 		    case 'F':
737 			dtrace.setOption(Option.flowindent);
738 			break;
739 		    case 'i':
740 			r = new CompileRequest();
741 			r.s = g.getOptarg();
742 			r.type = ProgramType.STRING;
743 			r.probespec = ProbeDescription.Spec.NAME;
744 			compileRequests.add(r);
745 			break;
746 		    case 'I':
747 			dtrace.setOption(Option.incdir, g.getOptarg());
748 			break;
749 		    case 'l':
750 			mode = Mode.LIST;
751 			dtrace.setOption(Option.zdefs); // -l implies -Z
752 			break;
753 		    case 'L':
754 			dtrace.setOption(Option.libdir, g.getOptarg());
755 			break;
756 		    case 'm':
757 			r = new CompileRequest();
758 			r.s = g.getOptarg();
759 			r.type = ProgramType.STRING;
760 			r.probespec = ProbeDescription.Spec.MODULE;
761 			compileRequests.add(r);
762 			break;
763 		    case 'n':
764 			r = new CompileRequest();
765 			r.s = g.getOptarg();
766 			r.type = ProgramType.STRING;
767 			r.probespec = ProbeDescription.Spec.NAME;
768 			compileRequests.add(r);
769 			break;
770 		    case 'o':
771 			String outFileName = g.getOptarg();
772 			File outFile = new File(outFileName);
773 			try {
774 			    FileOutputStream fos = new FileOutputStream(
775 				    outFile, true);
776 			    out = new PrintStream(fos);
777 			} catch (FileNotFoundException e) {
778 			    System.err.println("failed to open " +
779 				outFileName + " in write mode");
780 			    exit(1);
781 			} catch (SecurityException e) {
782 			    System.err.println("failed to open " +
783 				outFileName);
784 			    exit(1);
785 			}
786 			break;
787 		    case 'p':
788 			String pidstr = g.getOptarg();
789 			int pid = -1;
790 			try {
791 			    pid = Integer.parseInt(pidstr);
792 			} catch (NumberFormatException e) {
793 			    System.err.println("invalid pid: " + pidstr);
794 			    exit(1);
795 			}
796 			dtrace.grabProcess(pid);
797 			break;
798 		    case 'P':
799 			r = new CompileRequest();
800 			r.s = g.getOptarg();
801 			r.type = ProgramType.STRING;
802 			r.probespec = ProbeDescription.Spec.PROVIDER;
803 			compileRequests.add(r);
804 			break;
805 		    case 'q':
806 			dtrace.setOption(Option.quiet);
807 			break;
808 		    case 's':
809 			r = new CompileRequest();
810 			r.s = g.getOptarg();
811 			r.type = ProgramType.FILE;
812 			compileRequests.add(r);
813 			break;
814 		    case 'U':
815 			dtrace.setOption(Option.undef, g.getOptarg());
816 			break;
817 		    case 'v':
818 			verbose = true;
819 			break;
820 		    case 'V':
821 			mode = Mode.VERSION;
822 			break;
823 		    case 'w':
824 			dtrace.setOption(Option.destructive);
825 			break;
826 		    case 'x':
827 			String[] xarg = g.getOptarg().split("=", 2);
828 			if (xarg.length > 1) {
829 			    dtrace.setOption(xarg[0], xarg[1]);
830 			} else if (xarg.length == 1) {
831 			    dtrace.setOption(xarg[0]);
832 			}
833 			break;
834 		    case 'X':
835 			dtrace.setOption(Option.stdc, g.getOptarg());
836 			break;
837 		    case 'Z':
838 			dtrace.setOption(Option.zdefs);
839 			break;
840 		    case '?':
841 			usage(); // getopt() already printed an error
842 			break;
843 		    default:
844 			System.err.print("getopt() returned " + c + "\n");
845 			c = 0;
846 		 }
847 	    }
848 	    c = 0;
849 	    List <String> argList = new LinkedList <String> ();
850 	    for (int i = g.getOptind(); i < args.length; ++i) {
851 		argList.add(args[i]);
852 	    }
853 
854 	    if (mode == Mode.VERSION) {
855 		out.printf("dtrace: %s\n", dtrace.getVersion());
856 		dtrace.close();
857 		exit(0);
858 	    }
859 
860 	    String[] compileArgs = new String[argList.size()];
861 	    compileArgs = argList.toArray(compileArgs);
862 
863 	    Program program;
864 	    for (CompileRequest req : compileRequests) {
865 		switch (req.type) {
866 		    case STRING:
867 			applyProbespec(req);
868 			program = dtrace.compile(req.s, compileArgs);
869 			break;
870 		    case FILE:
871 			File file = new File(req.s);
872 			program = dtrace.compile(file, compileArgs);
873 			break;
874 		    default:
875 			throw new IllegalArgumentException(
876 				"Unexpected program type: " + req.type);
877 		}
878 
879 		programList.add(program);
880 	    }
881 
882 	    // Get options set by #pragmas in compiled program
883 	    long optval;
884 	    quiet = (dtrace.getOption(Option.quiet) != Option.UNSET);
885 	    flow = (dtrace.getOption(Option.flowindent) != Option.UNSET);
886 	    optval = dtrace.getOption("stackindent");
887 	    if (optval != Option.UNSET) {
888 		stackindent = (int)optval;
889 	    }
890 
891 	    if (mode == Mode.LIST) {
892 		out.printf("%5s %10s %17s %33s %s\n",
893 		    "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME");
894 
895 		if (verbose) {
896 		    List <List <Probe>> lists =
897 			    new LinkedList <List <Probe>> ();
898 		    for (Program p : programList) {
899 			lists.add(dtrace.listProgramProbeDetail(p));
900 		    }
901 		    ProbeDescription p;
902 		    ProbeInfo pinfo;
903 		    for (List <Probe> list : lists) {
904 			for (Probe probe : list) {
905 			    p = probe.getDescription();
906 			    pinfo = probe.getInfo();
907 			    printProbeDescription(p);
908 			    printProbeInfo(pinfo);
909 			}
910 		    }
911 		} else {
912 		    List <List <ProbeDescription>> lists =
913 			    new LinkedList <List <ProbeDescription>> ();
914 		    for (Program p : programList) {
915 			lists.add(dtrace.listProgramProbes(p));
916 		    }
917 		    for (List <ProbeDescription> list : lists) {
918 			for (ProbeDescription p : list) {
919 			    printProbeDescription(p);
920 			}
921 		    }
922 		}
923 		exit(0);
924 	    }
925 
926 	    String programType;
927 	    String programDescription;
928 	    ProgramInfo info;
929 	    for (Program p : programList) {
930 		if (p instanceof Program.File) {
931 		    Program.File pf = (Program.File)p;
932 		    programType = "script";
933 		    programDescription = pf.getFile().getPath();
934 		} else {
935 		    programType = "description";
936 		    programDescription =
937 			p.getContents().split("[/{;]", 2)[0];
938 		}
939 
940 		if (mode == Mode.EXEC) {
941 		    dtrace.enable(p);
942 		} else {
943 		    dtrace.getProgramInfo(p);
944 		}
945 		info = p.getInfo();
946 		if ((mode == Mode.EXEC) && !quiet) {
947 		    System.err.printf("dtrace: %s '%s' matched %d probe%s\n",
948 			    programType, programDescription,
949 			    info.getMatchingProbeCount(),
950 			    info.getMatchingProbeCount() == 1 ? "" : "s");
951 		}
952 		if (verbose) {
953 		    printProgramStability(programType,
954 			    programDescription, info);
955 		}
956 	    }
957 	    if (mode != Mode.EXEC) {
958 		exit(0);
959 	    }
960 	    dtrace.addConsumerListener(new ConsumerAdapter() {
961 		public void consumerStarted(ConsumerEvent e) {
962 		    started = true;
963 		}
964 		public void consumerStopped(ConsumerEvent e) {
965 		    stopped = true;
966 		    out.println();
967 		    try {
968 			Aggregate aggregate = dtrace.getAggregate();
969 			if (aggregate != null) {
970 			    printAggregate(aggregate);
971 			}
972 			dtrace.close();
973 		    } catch (Throwable x) {
974 			exceptionHandler.handleException(x);
975 		    }
976 		    exit(0);
977 		}
978 		public void dataDropped(DropEvent e) {
979 		    System.err.printf("dtrace: %s",
980 			    e.getDrop().getDefaultMessage());
981 		}
982 		public void errorEncountered(ErrorEvent e)
983 			throws ConsumerException {
984 		    org.opensolaris.os.dtrace.Error error = e.getError();
985 		    if (logger.isLoggable(Level.FINE)) {
986 			logger.fine(error.toString());
987 		    }
988 		    System.err.printf("dtrace: %s",
989 			    error.getDefaultMessage());
990 		}
991 		public void dataReceived(DataEvent e)
992 			throws ConsumerException {
993 		    consumeProbeData(e.getProbeData());
994 		}
995 		public void processStateChanged(ProcessEvent e)
996 			throws ConsumerException {
997 		    if (logger.isLoggable(Level.FINE)) {
998 			logger.fine(e.getProcessState().toString());
999 		    }
1000 		}
1001 	    });
1002 	    // Print unprinted aggregations after Ctrl-C
1003 	    Runtime.getRuntime().addShutdownHook(new Thread() {
1004 		public void run() {
1005 		    if (stopped || !started) {
1006 			return;
1007 		    }
1008 
1009 		    try {
1010 			Aggregate aggregate = dtrace.getAggregate();
1011 			if (aggregate != null) {
1012 			    out.println();
1013 			    out.println();
1014 			    printAggregate(aggregate);
1015 			}
1016 		    } catch (Throwable x) {
1017 			exceptionHandler.handleException(x);
1018 		    }
1019 		}
1020 	    });
1021 	    dtrace.go(exceptionHandler);
1022 	} catch (DTraceException e) {
1023 	    if (c > 0) {
1024 		// set option error
1025 		if (g.getOptarg() == null) {
1026 		    System.err.printf("dtrace: failed to set -%c: %s\n",
1027 			c, e.getMessage());
1028 		} else {
1029 		    System.err.printf("dtrace: failed to set -%c %s: %s\n",
1030 			c, g.getOptarg(), e.getMessage());
1031 		}
1032 	    } else {
1033 		// any other error
1034 		System.err.printf("dtrace: %s\n", e.getMessage());
1035 	    }
1036 	    exit(1);
1037 	} catch (Exception e) {
1038 	    e.printStackTrace();
1039 	    exit(1);
1040 	}
1041     }
1042 }
1043