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