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