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