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(1M)} 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 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 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 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 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 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 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 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 452 printAggregate(Aggregate aggregate) 453 { 454 printAggregationRecords(aggregate.getOrderedRecords()); 455 } 456 457 static void 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 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 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 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 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 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 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