1178476Sjb/*
2178476Sjb * CDDL HEADER START
3178476Sjb *
4178476Sjb * The contents of this file are subject to the terms of the
5178476Sjb * Common Development and Distribution License (the "License").
6178476Sjb * You may not use this file except in compliance with the License.
7178476Sjb *
8178476Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9178476Sjb * or http://www.opensolaris.org/os/licensing.
10178476Sjb * See the License for the specific language governing permissions
11178476Sjb * and limitations under the License.
12178476Sjb *
13178476Sjb * When distributing Covered Code, include this CDDL HEADER in each
14178476Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15178476Sjb * If applicable, add the following below this CDDL HEADER, with the
16178476Sjb * fields enclosed by brackets "[]" replaced with your own identifying
17178476Sjb * information: Portions Copyright [yyyy] [name of copyright owner]
18178476Sjb *
19178476Sjb * CDDL HEADER END
20178476Sjb */
21178476Sjb
22178476Sjb/*
23178476Sjb * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24178476Sjb * Use is subject to license terms.
25178476Sjb *
26178476Sjb * ident	"%Z%%M%	%I%	%E% SMI"
27178476Sjb */
28178476Sjbimport org.opensolaris.os.dtrace.*;
29178476Sjbimport java.io.*;
30178476Sjbimport java.util.*;
31178476Sjbimport java.util.logging.*;
32178476Sjb
33178476Sjb/**
34178476Sjb * Emulates {@code dtrace(1M)} using the Java DTrace API.
35178476Sjb */
36178476Sjbpublic class JDTrace {
37178476Sjb    static Logger logger = Logger.getLogger(JDTrace.class.getName());
38178476Sjb
39178476Sjb    static Consumer dtrace;
40178476Sjb
41178476Sjb    static {
42178476Sjb	Handler handler = new ConsoleHandler();
43178476Sjb	handler.setLevel(Level.ALL);
44178476Sjb	logger.addHandler(handler);
45178476Sjb    }
46178476Sjb
47178476Sjb    static final String CLASSNAME = "JDTrace";
48178476Sjb    static final String OPTSTR =
49178476Sjb	    "3:6:b:c:CD:ef:Fi:I:lL:m:n:o:p:P:qs:U:vVwx:X:Z";
50178476Sjb    static boolean heading = false;
51178476Sjb    static boolean quiet = false;
52178476Sjb    static boolean flow = false;
53178476Sjb    static int stackindent = 14;
54178476Sjb    static int exitStatus = 0;
55178476Sjb    static boolean started;
56178476Sjb    static boolean stopped;
57178476Sjb    static PrintStream out = System.out;
58178476Sjb    static final String ATS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
59178476Sjb    static final String SPACES = "                                        ";
60178476Sjb    static final int QUANTIZE_ZERO_BUCKET = 63;
61178476Sjb
62178476Sjb    enum Mode {
63178476Sjb	EXEC,
64178476Sjb	INFO,
65178476Sjb	LIST,
66178476Sjb	VERSION
67178476Sjb    }
68178476Sjb
69178476Sjb    enum ProgramType {
70178476Sjb	STRING,
71178476Sjb	FILE
72178476Sjb    }
73178476Sjb
74178476Sjb    static class CompileRequest {
75178476Sjb	String s;
76178476Sjb	ProgramType type;
77178476Sjb	ProbeDescription.Spec probespec;
78178476Sjb    }
79178476Sjb
80178476Sjb    // Modify program string by expanding an incomplete probe
81178476Sjb    // description according to the requested probespec.
82178476Sjb    static void
83178476Sjb    applyProbespec(CompileRequest req)
84178476Sjb    {
85178476Sjb	ProbeDescription.Spec spec = ((req.probespec == null)
86178476Sjb		? ProbeDescription.Spec.NAME
87178476Sjb		: req.probespec);
88178476Sjb
89178476Sjb	int colons = 0;
90178476Sjb	switch (req.probespec) {
91178476Sjb	    case PROVIDER:
92178476Sjb		colons = 3;
93178476Sjb		break;
94178476Sjb	    case MODULE:
95178476Sjb		colons = 2;
96178476Sjb		break;
97178476Sjb	    case FUNCTION:
98178476Sjb		colons = 1;
99178476Sjb		break;
100178476Sjb	}
101178476Sjb
102178476Sjb	StringBuffer buf = new StringBuffer();
103178476Sjb	if (colons > 0) {
104178476Sjb	    char ch;
105178476Sjb	    int len = req.s.length();
106178476Sjb
107178476Sjb	    int i = 0;
108178476Sjb	    // Find first whitespace character not including leading
109178476Sjb	    // whitespace (end of first token).  Ignore whitespace
110178476Sjb	    // inside a block if the block is concatenated with the
111178476Sjb	    // probe description.
112178476Sjb	    for (; (i < len) && Character.isWhitespace(req.s.charAt(i)); ++i);
113178476Sjb	    int npos = i;
114178476Sjb	    boolean inBlock = false;
115178476Sjb	    for (; (npos < len) &&
116178476Sjb		    (!Character.isWhitespace(ch = req.s.charAt(npos)) ||
117178476Sjb		    inBlock); ++npos) {
118178476Sjb		if (ch == '{') {
119178476Sjb		    inBlock = true;
120178476Sjb		} else if (ch == '}') {
121178476Sjb		    inBlock = false;
122178476Sjb		}
123178476Sjb	    }
124178476Sjb
125178476Sjb	    // libdtrace lets you concatenate multiple probe
126178476Sjb	    // descriptions separated by code blocks in curly braces,
127178476Sjb	    // for example genunix::'{printf("FOUND");}'::entry, as long
128178476Sjb	    // as the concatenated probe descriptions begin with ':' and
129178476Sjb	    // not a specific field such as 'syscall'.  So to expand the
130178476Sjb	    // possibly multiple probe descriptions, we need to insert
131178476Sjb	    // colons before each open curly brace, and again at the end
132178476Sjb	    // only if there is at least one non-whitespace (probe
133178476Sjb	    // specifying) character after the last closing curly brace.
134178476Sjb
135178476Sjb	    int prev_i = 0;
136178476Sjb	    while (i < npos) {
137178476Sjb		for (; (i < npos) && (req.s.charAt(i) != '{'); ++i);
138178476Sjb		buf.append(req.s.substring(prev_i, i));
139178476Sjb		if ((i < npos) || ((i > 0) && (req.s.charAt(i - 1) != '}'))) {
140178476Sjb		    for (int c = 0; c < colons; ++c) {
141178476Sjb			buf.append(':');
142178476Sjb		    }
143178476Sjb		}
144178476Sjb		if (i < npos) {
145178476Sjb		    buf.append(req.s.charAt(i++));
146178476Sjb		}
147178476Sjb		prev_i = i;
148178476Sjb	    }
149178476Sjb
150178476Sjb	    // append remainder of program text
151178476Sjb	    buf.append(req.s.substring(i));
152178476Sjb
153178476Sjb	    req.s = buf.toString();
154178476Sjb	}
155178476Sjb    }
156178476Sjb
157178476Sjb    static void
158178476Sjb    printValue(Object value, int bytes, String stringFormat)
159178476Sjb    {
160178476Sjb	if (value instanceof Integer) {
161178476Sjb	    if (bytes == 1) {
162178476Sjb		out.printf(" %3d", (Integer)value);
163178476Sjb	    } else if (bytes == 2) {
164178476Sjb		out.printf(" %5d", (Integer)value);
165178476Sjb	    } else {
166178476Sjb		out.printf(" %8d", (Integer)value);
167178476Sjb	    }
168178476Sjb	} else if (value instanceof Long) {
169178476Sjb	    out.printf(" %16d", (Long)value);
170178476Sjb	} else {
171178476Sjb	    out.printf(stringFormat, value.toString());
172178476Sjb	}
173178476Sjb    }
174178476Sjb
175178476Sjb    static void
176178476Sjb    consumeProbeData(ProbeData data)
177178476Sjb    {
178178476Sjb	if (logger.isLoggable(Level.FINER)) {
179178476Sjb	    logger.finer(data.toString());
180178476Sjb	}
181178476Sjb
182178476Sjb	if (!heading) {
183178476Sjb	    if (flow) {
184178476Sjb		out.printf("%3s %-41s\n", "CPU", "FUNCTION");
185178476Sjb	    } else {
186178476Sjb		if (!quiet) {
187178476Sjb		    out.printf("%3s %6s %32s\n",
188178476Sjb			    "CPU", "ID", "FUNCTION:NAME");
189178476Sjb		}
190178476Sjb	    }
191178476Sjb	    heading = true;
192178476Sjb	}
193178476Sjb	ProbeDescription probe = data.getEnabledProbeDescription();
194178476Sjb	if (flow) {
195178476Sjb	    Flow flow = data.getFlow();
196178476Sjb	    int indent = (flow.getDepth() * 2);
197178476Sjb	    StringBuffer buf = new StringBuffer();
198178476Sjb	    // indent
199178476Sjb	    buf.append(' ');
200178476Sjb	    for (int i = 0; i < indent; ++i) {
201178476Sjb		buf.append(' ');
202178476Sjb	    }
203178476Sjb	    // prefix
204178476Sjb	    switch (flow.getKind()) {
205178476Sjb		case ENTRY:
206178476Sjb		    if (indent == 0) {
207178476Sjb			buf.append("=> ");
208178476Sjb		    } else {
209178476Sjb			buf.append("-> ");
210178476Sjb		    }
211178476Sjb		    break;
212178476Sjb		case RETURN:
213178476Sjb		    if (indent == 0) {
214178476Sjb			buf.append("<= ");
215178476Sjb		    } else {
216178476Sjb			buf.append("<- ");
217178476Sjb		    }
218178476Sjb		    break;
219178476Sjb	    }
220178476Sjb
221178476Sjb	    switch (flow.getKind()) {
222178476Sjb		case NONE:
223178476Sjb		    buf.append(probe.getFunction());
224178476Sjb		    buf.append(':');
225178476Sjb		    buf.append(probe.getName());
226178476Sjb		    break;
227178476Sjb		default:
228178476Sjb		    buf.append(probe.getFunction());
229178476Sjb	    }
230178476Sjb
231178476Sjb	    out.printf("%3s %-41s ", data.getCPU(),
232178476Sjb		    buf.toString());
233178476Sjb	} else {
234178476Sjb	    if (!quiet) {
235178476Sjb		StringBuffer buf = new StringBuffer();
236178476Sjb		buf.append(probe.getFunction());
237178476Sjb		buf.append(':');
238178476Sjb		buf.append(probe.getName());
239178476Sjb		out.printf("%3s %6s %32s ",
240178476Sjb			data.getCPU(), probe.getID(),
241178476Sjb			buf.toString());
242178476Sjb	    }
243178476Sjb	}
244178476Sjb	Record record = null;
245178476Sjb	Object value;
246178476Sjb	List <Record> records = data.getRecords();
247178476Sjb	Iterator <Record> itr = records.iterator();
248178476Sjb	while (itr.hasNext()) {
249178476Sjb	    record = itr.next();
250178476Sjb
251178476Sjb	    if (record instanceof ExitRecord) {
252178476Sjb		exitStatus = ((ExitRecord)record).getStatus();
253178476Sjb	    } else if (record instanceof ScalarRecord) {
254178476Sjb		ScalarRecord scalar = (ScalarRecord)record;
255178476Sjb		value = scalar.getValue();
256178476Sjb		if (value instanceof byte[]) {
257178476Sjb		    out.print(record.toString());
258178476Sjb		} else {
259178476Sjb		    if (quiet) {
260178476Sjb			out.print(value);
261178476Sjb		    } else {
262178476Sjb			printValue(value, scalar.getNumberOfBytes(),
263178476Sjb				"  %-33s");
264178476Sjb		    }
265178476Sjb		}
266178476Sjb	    } else if (record instanceof PrintfRecord) {
267178476Sjb		out.print(record);
268178476Sjb	    } else if (record instanceof PrintaRecord) {
269178476Sjb		PrintaRecord printa = (PrintaRecord)record;
270178476Sjb		List <Tuple> tuples = printa.getTuples();
271178476Sjb		if (tuples.isEmpty()) {
272178476Sjb		    out.print(printa.getOutput());
273178476Sjb		} else {
274178476Sjb		    for (Tuple t : tuples) {
275178476Sjb			out.print(printa.getFormattedString(t));
276178476Sjb		    }
277178476Sjb		}
278178476Sjb
279178476Sjb		if (logger.isLoggable(Level.FINE)) {
280178476Sjb		    logger.fine(printa.toString());
281178476Sjb		}
282178476Sjb	    } else if (record instanceof StackValueRecord) {
283178476Sjb		printStack((StackValueRecord)record);
284178476Sjb	    }
285178476Sjb	}
286178476Sjb	if (!quiet) {
287178476Sjb	    out.println();
288178476Sjb	}
289178476Sjb    }
290178476Sjb
291178476Sjb    static void
292178476Sjb    printDistribution(Distribution d)
293178476Sjb    {
294178476Sjb	out.printf("\n%16s %41s %-9s\n", "value",
295178476Sjb		"------------- Distribution -------------",
296178476Sjb		"count");
297178476Sjb	long v; // bucket frequency (value)
298178476Sjb	long b; // lower bound of bucket range
299178476Sjb	double total = 0;
300178476Sjb	boolean positives = false;
301178476Sjb	boolean negatives = false;
302178476Sjb
303178476Sjb	Distribution.Bucket bucket;
304178476Sjb	int b1 = 0; // first displayed bucket
305178476Sjb	int b2 = d.size() - 1; // last displayed bucket
306178476Sjb	for (; (b1 <= b2) && (d.get(b1).getFrequency() == 0); ++b1);
307178476Sjb	// If possible, get one bucket before the first non-zero
308178476Sjb	// bucket and one bucket after the last.
309178476Sjb	if (b1 > b2) {
310178476Sjb	    // There isn't any data.  This is possible if (and only if)
311178476Sjb	    // negative increment values have been used.  In this case,
312178476Sjb	    // print the buckets around the base.
313178476Sjb	    if (d instanceof LinearDistribution) {
314178476Sjb		b1 = 0;
315178476Sjb		b2 = 2;
316178476Sjb	    } else {
317178476Sjb		b1 = QUANTIZE_ZERO_BUCKET - 1;
318178476Sjb		b2 = QUANTIZE_ZERO_BUCKET + 1;
319178476Sjb	    }
320178476Sjb	} else {
321178476Sjb	    if (b1 > 0) --b1;
322178476Sjb	    for (; (b2 > 0) && (d.get(b2).getFrequency() == 0); --b2);
323178476Sjb	    if (b2 < (d.size() - 1)) ++b2;
324178476Sjb	}
325178476Sjb	for (int i = b1; i <= b2; ++i) {
326178476Sjb	    v = d.get(i).getFrequency();
327178476Sjb	    if (v > 0) {
328178476Sjb		positives = true;
329178476Sjb	    }
330178476Sjb	    if (v < 0) {
331178476Sjb		negatives = true;
332178476Sjb	    }
333178476Sjb	    total += Math.abs((double)v);
334178476Sjb	}
335178476Sjb	for (int i = b1; i <= b2; ++i) {
336178476Sjb	    bucket = d.get(i);
337178476Sjb	    v = bucket.getFrequency();
338178476Sjb	    b = bucket.getMin();
339178476Sjb
340178476Sjb	    if (d instanceof LinearDistribution) {
341178476Sjb		if (b == Long.MIN_VALUE) {
342178476Sjb		    String lt = "< " + ((LinearDistribution)d).getBase();
343178476Sjb		    out.printf("%16s ", lt);
344178476Sjb		} else if (bucket.getMax() == Long.MAX_VALUE) {
345178476Sjb		    String ge = ">= " + b;
346178476Sjb		    out.printf("%16s ", ge);
347178476Sjb		} else {
348178476Sjb		    out.printf("%16d ", b);
349178476Sjb		}
350178476Sjb	    } else {
351178476Sjb		out.printf("%16d ", b);
352178476Sjb	    }
353178476Sjb
354178476Sjb	    printDistributionLine(v, total, positives, negatives);
355178476Sjb	}
356178476Sjb    }
357178476Sjb
358178476Sjb    static void
359178476Sjb    printDistributionLine(long val, double total, boolean positives,
360178476Sjb	    boolean negatives)
361178476Sjb    {
362178476Sjb	double f;
363178476Sjb	int depth, len = 40;
364178476Sjb
365178476Sjb	assert (ATS.length() == len && SPACES.length() == len);
366178476Sjb	assert (!(total == 0 && (positives || negatives)));
367178476Sjb	assert (!(val < 0 && !negatives));
368178476Sjb	assert (!(val > 0 && !positives));
369178476Sjb	assert (!(val != 0 && total == 0));
370178476Sjb
371178476Sjb	if (!negatives) {
372178476Sjb	    if (positives) {
373178476Sjb		f = (Math.abs((double)val) * (double)len) / total;
374178476Sjb		    depth = (int)(f + 0.5);
375178476Sjb	    } else {
376178476Sjb		depth = 0;
377178476Sjb	    }
378178476Sjb
379178476Sjb	    out.printf("|%s%s %-9d\n", ATS.substring(len - depth),
380178476Sjb		    SPACES.substring(depth), val);
381178476Sjb	    return;
382178476Sjb	}
383178476Sjb
384178476Sjb	if (!positives) {
385178476Sjb	    f = (Math.abs((double)val) * (double)len) / total;
386178476Sjb	    depth = (int)(f + 0.5);
387178476Sjb
388178476Sjb	    out.printf("%s%s| %-9d\n", SPACES.substring(depth),
389178476Sjb		    ATS.substring(len - depth), val);
390178476Sjb	    return;
391178476Sjb	}
392178476Sjb
393178476Sjb	/*
394178476Sjb	 * If we're here, we have both positive and negative bucket values.
395178476Sjb	 * To express this graphically, we're going to generate both positive
396178476Sjb	 * and negative bars separated by a centerline.  These bars are half
397178476Sjb	 * the size of normal quantize()/lquantize() bars, so we divide the
398178476Sjb	 * length in half before calculating the bar length.
399178476Sjb	 */
400178476Sjb	len /= 2;
401178476Sjb	String ats = ATS.substring(len);
402178476Sjb	String spaces = SPACES.substring(len);
403178476Sjb
404178476Sjb	f = (Math.abs((double)val) * (double)len) / total;
405178476Sjb	depth = (int)(f + 0.5);
406178476Sjb
407178476Sjb	if (val <= 0) {
408178476Sjb	    out.printf("%s%s|%s %-9d\n", spaces.substring(depth),
409178476Sjb		    ats.substring(len - depth), repeat(" ", len), val);
410178476Sjb	    return;
411178476Sjb	} else {
412178476Sjb	    out.printf("%20s|%s%s %-9d\n", "", ats.substring(len - depth),
413178476Sjb		    spaces.substring(depth), val);
414178476Sjb	}
415178476Sjb    }
416178476Sjb
417178476Sjb    public static String
418178476Sjb    repeat(String s, int n)
419178476Sjb    {
420178476Sjb        StringBuffer buf = new StringBuffer();
421178476Sjb        for (int i = 0; i < n; ++i) {
422178476Sjb            buf.append(s);
423178476Sjb        }
424178476Sjb        return buf.toString();
425178476Sjb    }
426178476Sjb
427178476Sjb    static void
428178476Sjb    printStack(StackValueRecord rec)
429178476Sjb    {
430178476Sjb	StackFrame[] frames = rec.getStackFrames();
431178476Sjb	int i;
432178476Sjb	out.println();
433178476Sjb	String s;
434178476Sjb	for (StackFrame f : frames) {
435178476Sjb	    for (i = 0; i < stackindent; ++i) {
436178476Sjb		out.print(' ');
437178476Sjb	    }
438178476Sjb	    s = f.getFrame();
439178476Sjb	    if (s.indexOf('[') == 0) {
440178476Sjb		out.print("  ");
441178476Sjb	    }
442178476Sjb	    out.println(s);
443178476Sjb	}
444178476Sjb    }
445178476Sjb
446178476Sjb    static void
447178476Sjb    printAggregate(Aggregate aggregate)
448178476Sjb    {
449178476Sjb	printAggregationRecords(aggregate.getOrderedRecords());
450178476Sjb    }
451178476Sjb
452178476Sjb    static void
453178476Sjb    printAggregationRecords(List <AggregationRecord> list)
454178476Sjb    {
455178476Sjb	Tuple tuple;
456178476Sjb	AggregationValue value;
457178476Sjb	ValueRecord tupleRecord;
458178476Sjb	int i;
459178476Sjb	int len;
460178476Sjb	for (AggregationRecord r : list) {
461178476Sjb	    tuple = r.getTuple();
462178476Sjb	    value = r.getValue();
463178476Sjb	    len = tuple.size();
464178476Sjb	    for (i = 0; i < len; ++i) {
465178476Sjb		tupleRecord = tuple.get(i);
466178476Sjb		if (tupleRecord instanceof StackValueRecord) {
467178476Sjb		    printStack((StackValueRecord)tupleRecord);
468178476Sjb		} else if (tupleRecord instanceof SymbolValueRecord) {
469178476Sjb		    printValue(tupleRecord.toString(), -1, "  %-50s");
470178476Sjb		} else {
471178476Sjb		    printValue(tupleRecord.getValue(),
472178476Sjb			    ((ScalarRecord)tupleRecord).getNumberOfBytes(),
473178476Sjb			    "  %-50s");
474178476Sjb		}
475178476Sjb	    }
476178476Sjb	    if (value instanceof Distribution) {
477178476Sjb		Distribution d = (Distribution)value;
478178476Sjb		printDistribution(d);
479178476Sjb	    } else {
480178476Sjb		Number v = value.getValue();
481178476Sjb		printValue(v, -1, "  %-50s");
482178476Sjb	    }
483178476Sjb	    out.println();
484178476Sjb	}
485178476Sjb    }
486178476Sjb
487178476Sjb    static void
488178476Sjb    exit(int status)
489178476Sjb    {
490178476Sjb	out.flush();
491178476Sjb	System.err.flush();
492178476Sjb	if (status == 0) {
493178476Sjb	    status = exitStatus;
494178476Sjb	}
495178476Sjb	System.exit(status);
496178476Sjb    }
497178476Sjb
498178476Sjb    static void
499178476Sjb    usage()
500178476Sjb    {
501178476Sjb	String predact = "[[ predicate ] action ]";
502178476Sjb	System.err.printf("Usage: java %s [-32|-64] [-CeFlqvVwZ] " +
503178476Sjb	    "[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] " +
504178476Sjb	    "[-o output] [-p pid] [-s script] [-U name]\n\t" +
505178476Sjb	    "[-x opt[=val]] [-X a|c|s|t]\n\n" +
506178476Sjb	    "\t[-P provider %s]\n" +
507178476Sjb	    "\t[-m [ provider: ] module %s]\n" +
508178476Sjb	    "\t[-f [[ provider: ] module: ] func %s]\n" +
509178476Sjb	    "\t[-n [[[ provider: ] module: ] func: ] name %s]\n" +
510178476Sjb	    "\t[-i probe-id %s] [ args ... ]\n\n", CLASSNAME,
511178476Sjb	    predact, predact, predact, predact, predact);
512178476Sjb	System.err.printf("\tpredicate -> '/' D-expression '/'\n");
513178476Sjb	System.err.printf("\t   action -> '{' D-statements '}'\n");
514178476Sjb	System.err.printf("\n" +
515178476Sjb	    "\t-32 generate 32-bit D programs\n" +
516178476Sjb	    "\t-64 generate 64-bit D programs\n\n" +
517178476Sjb	    "\t-b  set trace buffer size\n" +
518178476Sjb	    "\t-c  run specified command and exit upon its completion\n" +
519178476Sjb	    "\t-C  run cpp(1) preprocessor on script files\n" +
520178476Sjb	    "\t-D  define symbol when invoking preprocessor\n" +
521178476Sjb	    "\t-e  exit after compiling request but prior to enabling " +
522178476Sjb		    "probes\n" +
523178476Sjb	    "\t-f  enable or list probes matching the specified " +
524178476Sjb		    "function name\n" +
525178476Sjb	    "\t-F  coalesce trace output by function\n" +
526178476Sjb	    "\t-i  enable or list probes matching the specified probe id\n" +
527178476Sjb	    "\t-I  add include directory to preprocessor search path\n" +
528178476Sjb	    "\t-l  list probes matching specified criteria\n" +
529178476Sjb	    "\t-L  add library directory to library search path\n" +
530178476Sjb	    "\t-m  enable or list probes matching the specified " +
531178476Sjb		    "module name\n" +
532178476Sjb	    "\t-n  enable or list probes matching the specified probe name\n" +
533178476Sjb	    "\t-o  set output file\n" +
534178476Sjb	    "\t-p  grab specified process-ID and cache its symbol tables\n" +
535178476Sjb	    "\t-P  enable or list probes matching the specified " +
536178476Sjb		    "provider name\n" +
537178476Sjb	    "\t-q  set quiet mode (only output explicitly traced data)\n" +
538178476Sjb	    "\t-s  enable or list probes according to the specified " +
539178476Sjb		    "D script\n" +
540178476Sjb	    "\t-U  undefine symbol when invoking preprocessor\n" +
541178476Sjb	    "\t-v  set verbose mode (report stability attributes, " +
542178476Sjb		    "arguments)\n" +
543178476Sjb	    "\t-V  report DTrace API version\n" +
544178476Sjb	    "\t-w  permit destructive actions\n" +
545178476Sjb	    "\t-x  enable or modify compiler and tracing options\n" +
546178476Sjb	    "\t-X  specify ISO C conformance settings for preprocessor\n" +
547178476Sjb	    "\t-Z  permit probe descriptions that match zero probes\n" +
548178476Sjb	    "\n" +
549178476Sjb	    "\tTo log PrintaRecord, set this environment variable:\n" +
550178476Sjb	    "\t\tJDTRACE_LOGGING_LEVEL=FINE\n" +
551178476Sjb	    "\tTo log ProbeData, set JDTRACE_LOGGING_LEVEL=FINER\n");
552178476Sjb	exit(2);
553178476Sjb    }
554178476Sjb
555178476Sjb    static void
556178476Sjb    printProgramStability(String programType, String programDescription,
557178476Sjb	    ProgramInfo info)
558178476Sjb    {
559178476Sjb	out.println();
560178476Sjb	out.printf("Stability data for %s %s:\n\n",
561178476Sjb		programType, programDescription);
562178476Sjb	InterfaceAttributes a;
563178476Sjb	out.println("\tMinimum probe description " +
564178476Sjb		"attributes");
565178476Sjb	a = info.getMinimumProbeAttributes();
566178476Sjb	out.printf("\t\tIdentifier Names: %s\n",
567178476Sjb		a.getNameStability());
568178476Sjb	out.printf("\t\tData Semantics:   %s\n",
569178476Sjb		a.getDataStability());
570178476Sjb	out.printf("\t\tDependency Class: %s\n",
571178476Sjb		a.getDependencyClass());
572178476Sjb	out.println("\tMinimum probe statement attributes");
573178476Sjb	a = info.getMinimumStatementAttributes();
574178476Sjb	out.printf("\t\tIdentifier Names: %s\n",
575178476Sjb		a.getNameStability());
576178476Sjb	out.printf("\t\tData Semantics:   %s\n",
577178476Sjb		a.getDataStability());
578178476Sjb	out.printf("\t\tDependency Class: %s\n",
579178476Sjb		a.getDependencyClass());
580178476Sjb    }
581178476Sjb
582178476Sjb    static void
583178476Sjb    printProbeDescription(ProbeDescription p)
584178476Sjb    {
585178476Sjb	out.printf("%5d %10s %17s %33s %s\n", p.getID(),
586178476Sjb	    p.getProvider(), p.getModule(),
587178476Sjb	    p.getFunction(), p.getName());
588178476Sjb    }
589178476Sjb
590178476Sjb    static void
591178476Sjb    printProbeInfo(ProbeInfo p)
592178476Sjb    {
593178476Sjb	InterfaceAttributes a;
594178476Sjb	out.println("\n\tProbe Description Attributes");
595178476Sjb
596178476Sjb	a = p.getProbeAttributes();
597178476Sjb	out.printf("\t\tIdentifier Names: %s\n",
598178476Sjb	    a.getNameStability());
599178476Sjb	out.printf("\t\tData Semantics:   %s\n",
600178476Sjb	    a.getDataStability());
601178476Sjb	out.printf("\t\tDependency Class: %s\n",
602178476Sjb	    a.getDependencyClass());
603178476Sjb
604178476Sjb	out.println("\n\tArgument Attributes");
605178476Sjb
606178476Sjb	a = p.getArgumentAttributes();
607178476Sjb	out.printf("\t\tIdentifier Names: %s\n",
608178476Sjb	    a.getNameStability());
609178476Sjb	out.printf("\t\tData Semantics:   %s\n",
610178476Sjb	    a.getDataStability());
611178476Sjb	out.printf("\t\tDependency Class: %s\n",
612178476Sjb	    a.getDependencyClass());
613178476Sjb
614178476Sjb	// Argument types unsupported for now.
615178476Sjb
616178476Sjb	out.println();
617178476Sjb    }
618178476Sjb
619178476Sjb    public static void
620178476Sjb    main(String[] args)
621178476Sjb    {
622178476Sjb	String loggingLevel = System.getenv().get("JDTRACE_LOGGING_LEVEL");
623178476Sjb	try {
624178476Sjb	    logger.setLevel(Level.parse(loggingLevel));
625178476Sjb	} catch (Exception e) {
626178476Sjb	    logger.setLevel(Level.OFF);
627178476Sjb	}
628178476Sjb
629178476Sjb	if (args.length == 0) {
630178476Sjb	    usage();
631178476Sjb	}
632178476Sjb
633178476Sjb	List <CompileRequest> compileRequests = new LinkedList
634178476Sjb		<CompileRequest> ();
635178476Sjb	List <Program> programList = new LinkedList <Program> ();
636178476Sjb	boolean verbose = false;
637178476Sjb	Mode mode = Mode.EXEC;
638178476Sjb
639178476Sjb	final ExceptionHandler exceptionHandler = new ExceptionHandler() {
640178476Sjb	    public void handleException(Throwable e) {
641178476Sjb		if (e instanceof DTraceException) {
642178476Sjb		    DTraceException de = (DTraceException)e;
643178476Sjb		    System.err.printf("dtrace: %s\n", de.getMessage());
644178476Sjb		} else if (e instanceof ConsumerException) {
645178476Sjb		    ConsumerException ce = (ConsumerException)e;
646178476Sjb		    Object msg = ce.getNotificationObject();
647178476Sjb		    if ((msg instanceof org.opensolaris.os.dtrace.Error) ||
648178476Sjb			(msg instanceof Drop)) {
649178476Sjb			System.err.printf("dtrace: %s\n", ce.getMessage());
650178476Sjb		    } else {
651178476Sjb			ce.printStackTrace();
652178476Sjb		    }
653178476Sjb		} else {
654178476Sjb		    e.printStackTrace();
655178476Sjb		}
656178476Sjb		exit(1);
657178476Sjb	    }
658178476Sjb	};
659178476Sjb
660178476Sjb	Getopt g = new Getopt(CLASSNAME, args, OPTSTR);
661178476Sjb	int c = 0;
662178476Sjb
663178476Sjb	List <Consumer.OpenFlag> openFlags =
664178476Sjb		new ArrayList <Consumer.OpenFlag> ();
665178476Sjb
666178476Sjb	while ((c = g.getopt()) != -1) {
667178476Sjb	    switch (c) {
668178476Sjb		case '3': {
669178476Sjb		    String s = g.getOptarg();
670178476Sjb		    if (!s.equals("2")) {
671178476Sjb			System.err.println("dtrace: illegal option -- 3" + s);
672178476Sjb			usage();
673178476Sjb		    }
674178476Sjb		    openFlags.add(Consumer.OpenFlag.ILP32);
675178476Sjb		    break;
676178476Sjb		}
677178476Sjb		case '6': {
678178476Sjb		    String s = g.getOptarg();
679178476Sjb		    if (!s.equals("4")) {
680178476Sjb			System.err.println("dtrace: illegal option -- 6" + s);
681178476Sjb			usage();
682178476Sjb		    }
683178476Sjb		    openFlags.add(Consumer.OpenFlag.LP64);
684178476Sjb		    break;
685178476Sjb		}
686178476Sjb	    }
687178476Sjb	}
688178476Sjb
689178476Sjb	Consumer.OpenFlag[] oflags = new Consumer.OpenFlag[openFlags.size()];
690178476Sjb	oflags = openFlags.toArray(oflags);
691178476Sjb
692178476Sjb	dtrace = new LocalConsumer() {
693178476Sjb	    protected Thread createThread() {
694178476Sjb		Thread t = super.createThread();
695178476Sjb		t.setDaemon(false);
696178476Sjb		t.setPriority(Thread.MIN_PRIORITY);
697178476Sjb		return t;
698178476Sjb	    }
699178476Sjb	};
700178476Sjb
701178476Sjb	g = new Getopt(CLASSNAME, args, OPTSTR);
702178476Sjb	c = 0;
703178476Sjb
704178476Sjb	try {
705178476Sjb	    dtrace.open(oflags);
706178476Sjb
707178476Sjb	    // Set default options that may be overriden by options or #pragma
708178476Sjb	    dtrace.setOption(Option.bufsize, Option.mb(4));
709178476Sjb	    dtrace.setOption(Option.aggsize, Option.mb(4));
710178476Sjb
711178476Sjb	    CompileRequest r;
712178476Sjb	    while ((c = g.getopt()) != -1) {
713178476Sjb		switch (c) {
714178476Sjb		    case 'b':
715178476Sjb			dtrace.setOption(Option.bufsize, g.getOptarg());
716178476Sjb			break;
717178476Sjb		    case 'c':
718178476Sjb			dtrace.createProcess(g.getOptarg());
719178476Sjb			break;
720178476Sjb		    case 'C':
721178476Sjb			dtrace.setOption(Option.cpp);
722178476Sjb			break;
723178476Sjb		    case 'D':
724178476Sjb			dtrace.setOption(Option.define, g.getOptarg());
725178476Sjb			break;
726178476Sjb		    case 'e':
727178476Sjb			mode = Mode.INFO;
728178476Sjb			break;
729178476Sjb		    case 'f':
730178476Sjb			r = new CompileRequest();
731178476Sjb			r.s = g.getOptarg();
732178476Sjb			r.type = ProgramType.STRING;
733178476Sjb			r.probespec = ProbeDescription.Spec.FUNCTION;
734178476Sjb			compileRequests.add(r);
735178476Sjb			break;
736178476Sjb		    case 'F':
737178476Sjb			dtrace.setOption(Option.flowindent);
738178476Sjb			break;
739178476Sjb		    case 'i':
740178476Sjb			r = new CompileRequest();
741178476Sjb			r.s = g.getOptarg();
742178476Sjb			r.type = ProgramType.STRING;
743178476Sjb			r.probespec = ProbeDescription.Spec.NAME;
744178476Sjb			compileRequests.add(r);
745178476Sjb			break;
746178476Sjb		    case 'I':
747178476Sjb			dtrace.setOption(Option.incdir, g.getOptarg());
748178476Sjb			break;
749178476Sjb		    case 'l':
750178476Sjb			mode = Mode.LIST;
751178476Sjb			dtrace.setOption(Option.zdefs); // -l implies -Z
752178476Sjb			break;
753178476Sjb		    case 'L':
754178476Sjb			dtrace.setOption(Option.libdir, g.getOptarg());
755178476Sjb			break;
756178476Sjb		    case 'm':
757178476Sjb			r = new CompileRequest();
758178476Sjb			r.s = g.getOptarg();
759178476Sjb			r.type = ProgramType.STRING;
760178476Sjb			r.probespec = ProbeDescription.Spec.MODULE;
761178476Sjb			compileRequests.add(r);
762178476Sjb			break;
763178476Sjb		    case 'n':
764178476Sjb			r = new CompileRequest();
765178476Sjb			r.s = g.getOptarg();
766178476Sjb			r.type = ProgramType.STRING;
767178476Sjb			r.probespec = ProbeDescription.Spec.NAME;
768178476Sjb			compileRequests.add(r);
769178476Sjb			break;
770178476Sjb		    case 'o':
771178476Sjb			String outFileName = g.getOptarg();
772178476Sjb			File outFile = new File(outFileName);
773178476Sjb			try {
774178476Sjb			    FileOutputStream fos = new FileOutputStream(
775178476Sjb				    outFile, true);
776178476Sjb			    out = new PrintStream(fos);
777178476Sjb			} catch (FileNotFoundException e) {
778178476Sjb			    System.err.println("failed to open " +
779178476Sjb				outFileName + " in write mode");
780178476Sjb			    exit(1);
781178476Sjb			} catch (SecurityException e) {
782178476Sjb			    System.err.println("failed to open " +
783178476Sjb				outFileName);
784178476Sjb			    exit(1);
785178476Sjb			}
786178476Sjb			break;
787178476Sjb		    case 'p':
788178476Sjb			String pidstr = g.getOptarg();
789178476Sjb			int pid = -1;
790178476Sjb			try {
791178476Sjb			    pid = Integer.parseInt(pidstr);
792178476Sjb			} catch (NumberFormatException e) {
793178476Sjb			    System.err.println("invalid pid: " + pidstr);
794178476Sjb			    exit(1);
795178476Sjb			}
796178476Sjb			dtrace.grabProcess(pid);
797178476Sjb			break;
798178476Sjb		    case 'P':
799178476Sjb			r = new CompileRequest();
800178476Sjb			r.s = g.getOptarg();
801178476Sjb			r.type = ProgramType.STRING;
802178476Sjb			r.probespec = ProbeDescription.Spec.PROVIDER;
803178476Sjb			compileRequests.add(r);
804178476Sjb			break;
805178476Sjb		    case 'q':
806178476Sjb			dtrace.setOption(Option.quiet);
807178476Sjb			break;
808178476Sjb		    case 's':
809178476Sjb			r = new CompileRequest();
810178476Sjb			r.s = g.getOptarg();
811178476Sjb			r.type = ProgramType.FILE;
812178476Sjb			compileRequests.add(r);
813178476Sjb			break;
814178476Sjb		    case 'U':
815178476Sjb			dtrace.setOption(Option.undef, g.getOptarg());
816178476Sjb			break;
817178476Sjb		    case 'v':
818178476Sjb			verbose = true;
819178476Sjb			break;
820178476Sjb		    case 'V':
821178476Sjb			mode = Mode.VERSION;
822178476Sjb			break;
823178476Sjb		    case 'w':
824178476Sjb			dtrace.setOption(Option.destructive);
825178476Sjb			break;
826178476Sjb		    case 'x':
827178476Sjb			String[] xarg = g.getOptarg().split("=", 2);
828178476Sjb			if (xarg.length > 1) {
829178476Sjb			    dtrace.setOption(xarg[0], xarg[1]);
830178476Sjb			} else if (xarg.length == 1) {
831178476Sjb			    dtrace.setOption(xarg[0]);
832178476Sjb			}
833178476Sjb			break;
834178476Sjb		    case 'X':
835178476Sjb			dtrace.setOption(Option.stdc, g.getOptarg());
836178476Sjb			break;
837178476Sjb		    case 'Z':
838178476Sjb			dtrace.setOption(Option.zdefs);
839178476Sjb			break;
840178476Sjb		    case '?':
841178476Sjb			usage(); // getopt() already printed an error
842178476Sjb			break;
843178476Sjb		    default:
844178476Sjb			System.err.print("getopt() returned " + c + "\n");
845178476Sjb			c = 0;
846178476Sjb		 }
847178476Sjb	    }
848178476Sjb	    c = 0;
849178476Sjb	    List <String> argList = new LinkedList <String> ();
850178476Sjb	    for (int i = g.getOptind(); i < args.length; ++i) {
851178476Sjb		argList.add(args[i]);
852178476Sjb	    }
853178476Sjb
854178476Sjb	    if (mode == Mode.VERSION) {
855178476Sjb		out.printf("dtrace: %s\n", dtrace.getVersion());
856178476Sjb		dtrace.close();
857178476Sjb		exit(0);
858178476Sjb	    }
859178476Sjb
860178476Sjb	    String[] compileArgs = new String[argList.size()];
861178476Sjb	    compileArgs = argList.toArray(compileArgs);
862178476Sjb
863178476Sjb	    Program program;
864178476Sjb	    for (CompileRequest req : compileRequests) {
865178476Sjb		switch (req.type) {
866178476Sjb		    case STRING:
867178476Sjb			applyProbespec(req);
868178476Sjb			program = dtrace.compile(req.s, compileArgs);
869178476Sjb			break;
870178476Sjb		    case FILE:
871178476Sjb			File file = new File(req.s);
872178476Sjb			program = dtrace.compile(file, compileArgs);
873178476Sjb			break;
874178476Sjb		    default:
875178476Sjb			throw new IllegalArgumentException(
876178476Sjb				"Unexpected program type: " + req.type);
877178476Sjb		}
878178476Sjb
879178476Sjb		programList.add(program);
880178476Sjb	    }
881178476Sjb
882178476Sjb	    // Get options set by #pragmas in compiled program
883178476Sjb	    long optval;
884178476Sjb	    quiet = (dtrace.getOption(Option.quiet) != Option.UNSET);
885178476Sjb	    flow = (dtrace.getOption(Option.flowindent) != Option.UNSET);
886178476Sjb	    optval = dtrace.getOption("stackindent");
887178476Sjb	    if (optval != Option.UNSET) {
888178476Sjb		stackindent = (int)optval;
889178476Sjb	    }
890178476Sjb
891178476Sjb	    if (mode == Mode.LIST) {
892178476Sjb		out.printf("%5s %10s %17s %33s %s\n",
893178476Sjb		    "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME");
894178476Sjb
895178476Sjb		if (verbose) {
896178476Sjb		    List <List <Probe>> lists =
897178476Sjb			    new LinkedList <List <Probe>> ();
898178476Sjb		    for (Program p : programList) {
899178476Sjb			lists.add(dtrace.listProgramProbeDetail(p));
900178476Sjb		    }
901178476Sjb		    ProbeDescription p;
902178476Sjb		    ProbeInfo pinfo;
903178476Sjb		    for (List <Probe> list : lists) {
904178476Sjb			for (Probe probe : list) {
905178476Sjb			    p = probe.getDescription();
906178476Sjb			    pinfo = probe.getInfo();
907178476Sjb			    printProbeDescription(p);
908178476Sjb			    printProbeInfo(pinfo);
909178476Sjb			}
910178476Sjb		    }
911178476Sjb		} else {
912178476Sjb		    List <List <ProbeDescription>> lists =
913178476Sjb			    new LinkedList <List <ProbeDescription>> ();
914178476Sjb		    for (Program p : programList) {
915178476Sjb			lists.add(dtrace.listProgramProbes(p));
916178476Sjb		    }
917178476Sjb		    for (List <ProbeDescription> list : lists) {
918178476Sjb			for (ProbeDescription p : list) {
919178476Sjb			    printProbeDescription(p);
920178476Sjb			}
921178476Sjb		    }
922178476Sjb		}
923178476Sjb		exit(0);
924178476Sjb	    }
925178476Sjb
926178476Sjb	    String programType;
927178476Sjb	    String programDescription;
928178476Sjb	    ProgramInfo info;
929178476Sjb	    for (Program p : programList) {
930178476Sjb		if (p instanceof Program.File) {
931178476Sjb		    Program.File pf = (Program.File)p;
932178476Sjb		    programType = "script";
933178476Sjb		    programDescription = pf.getFile().getPath();
934178476Sjb		} else {
935178476Sjb		    programType = "description";
936178476Sjb		    programDescription =
937178476Sjb			p.getContents().split("[/{;]", 2)[0];
938178476Sjb		}
939178476Sjb
940178476Sjb		if (mode == Mode.EXEC) {
941178476Sjb		    dtrace.enable(p);
942178476Sjb		} else {
943178476Sjb		    dtrace.getProgramInfo(p);
944178476Sjb		}
945178476Sjb		info = p.getInfo();
946178476Sjb		if ((mode == Mode.EXEC) && !quiet) {
947178476Sjb		    System.err.printf("dtrace: %s '%s' matched %d probe%s\n",
948178476Sjb			    programType, programDescription,
949178476Sjb			    info.getMatchingProbeCount(),
950178476Sjb			    info.getMatchingProbeCount() == 1 ? "" : "s");
951178476Sjb		}
952178476Sjb		if (verbose) {
953178476Sjb		    printProgramStability(programType,
954178476Sjb			    programDescription, info);
955178476Sjb		}
956178476Sjb	    }
957178476Sjb	    if (mode != Mode.EXEC) {
958178476Sjb		exit(0);
959178476Sjb	    }
960178476Sjb	    dtrace.addConsumerListener(new ConsumerAdapter() {
961178476Sjb		public void consumerStarted(ConsumerEvent e) {
962178476Sjb		    started = true;
963178476Sjb		}
964178476Sjb		public void consumerStopped(ConsumerEvent e) {
965178476Sjb		    stopped = true;
966178476Sjb		    out.println();
967178476Sjb		    try {
968178476Sjb			Aggregate aggregate = dtrace.getAggregate();
969178476Sjb			if (aggregate != null) {
970178476Sjb			    printAggregate(aggregate);
971178476Sjb			}
972178476Sjb			dtrace.close();
973178476Sjb		    } catch (Throwable x) {
974178476Sjb			exceptionHandler.handleException(x);
975178476Sjb		    }
976178476Sjb		    exit(0);
977178476Sjb		}
978178476Sjb		public void dataDropped(DropEvent e) {
979178476Sjb		    System.err.printf("dtrace: %s",
980178476Sjb			    e.getDrop().getDefaultMessage());
981178476Sjb		}
982178476Sjb		public void errorEncountered(ErrorEvent e)
983178476Sjb			throws ConsumerException {
984178476Sjb		    org.opensolaris.os.dtrace.Error error = e.getError();
985178476Sjb		    if (logger.isLoggable(Level.FINE)) {
986178476Sjb			logger.fine(error.toString());
987178476Sjb		    }
988178476Sjb		    System.err.printf("dtrace: %s",
989178476Sjb			    error.getDefaultMessage());
990178476Sjb		}
991178476Sjb		public void dataReceived(DataEvent e)
992178476Sjb			throws ConsumerException {
993178476Sjb		    consumeProbeData(e.getProbeData());
994178476Sjb		}
995178476Sjb		public void processStateChanged(ProcessEvent e)
996178476Sjb			throws ConsumerException {
997178476Sjb		    if (logger.isLoggable(Level.FINE)) {
998178476Sjb			logger.fine(e.getProcessState().toString());
999178476Sjb		    }
1000178476Sjb		}
1001178476Sjb	    });
1002178476Sjb	    // Print unprinted aggregations after Ctrl-C
1003178476Sjb	    Runtime.getRuntime().addShutdownHook(new Thread() {
1004178476Sjb		public void run() {
1005178476Sjb		    if (stopped || !started) {
1006178476Sjb			return;
1007178476Sjb		    }
1008178476Sjb
1009178476Sjb		    try {
1010178476Sjb			Aggregate aggregate = dtrace.getAggregate();
1011178476Sjb			if (aggregate != null) {
1012178476Sjb			    out.println();
1013178476Sjb			    out.println();
1014178476Sjb			    printAggregate(aggregate);
1015178476Sjb			}
1016178476Sjb		    } catch (Throwable x) {
1017178476Sjb			exceptionHandler.handleException(x);
1018178476Sjb		    }
1019178476Sjb		}
1020178476Sjb	    });
1021178476Sjb	    dtrace.go(exceptionHandler);
1022178476Sjb	} catch (DTraceException e) {
1023178476Sjb	    if (c > 0) {
1024178476Sjb		// set option error
1025178476Sjb		if (g.getOptarg() == null) {
1026178476Sjb		    System.err.printf("dtrace: failed to set -%c: %s\n",
1027178476Sjb			c, e.getMessage());
1028178476Sjb		} else {
1029178476Sjb		    System.err.printf("dtrace: failed to set -%c %s: %s\n",
1030178476Sjb			c, g.getOptarg(), e.getMessage());
1031178476Sjb		}
1032178476Sjb	    } else {
1033178476Sjb		// any other error
1034178476Sjb		System.err.printf("dtrace: %s\n", e.getMessage());
1035178476Sjb	    }
1036178476Sjb	    exit(1);
1037178476Sjb	} catch (Exception e) {
1038178476Sjb	    e.printStackTrace();
1039178476Sjb	    exit(1);
1040178476Sjb	}
1041178476Sjb    }
1042178476Sjb}
1043