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 */
28import org.opensolaris.os.dtrace.*;
29import java.io.*;
30import java.util.*;
31import java.util.logging.*;
32
33/**
34 * Emulates {@code dtrace(1M)} using the Java DTrace API.
35 */
36public 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