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 2006 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 int 447 compareTuples(Tuple t1, Tuple t2, int pos) 448 { 449 int cmp = 0; 450 int len1 = t1.size(); 451 int len2 = t2.size(); 452 int index; 453 454 for (int i = 0; (cmp == 0) && (i < len1 && i < len2); ++i) { 455 index = i + pos; 456 if (index >= len1) { 457 index = index - len1; 458 } 459 cmp = Tuple.compare(t1, t2, index); 460 } 461 462 if (cmp == 0) { 463 cmp = (len1 < len2 ? -1 : (len1 > len2 ? 1 : 0)); 464 } 465 466 return cmp; 467 } 468 469 static int 470 compareValues(AggregationValue v1, AggregationValue v2) 471 { 472 int cmp; 473 474 if ((v1 instanceof LinearDistribution) && 475 (v2 instanceof LinearDistribution)) { 476 LinearDistribution l1 = (LinearDistribution)v1; 477 LinearDistribution l2 = (LinearDistribution)v2; 478 cmp = l1.compareTo(l2); 479 } else if ((v1 instanceof LogDistribution) && 480 (v2 instanceof LogDistribution)) { 481 LogDistribution l1 = (LogDistribution)v1; 482 LogDistribution l2 = (LogDistribution)v2; 483 cmp = l1.compareTo(l2); 484 } else { 485 double n1 = v1.getValue().doubleValue(); 486 double n2 = v2.getValue().doubleValue(); 487 cmp = (n1 < n2 ? -1 : (n1 > n2 ? 1 : 0)); 488 } 489 490 return cmp; 491 } 492 493 static Comparator <Object[]> 494 getAggValCmp(final int keypos) 495 { 496 Comparator <Object[]> CMP = new Comparator <Object[]> () { 497 public int 498 compare(Object[] pair1, Object[] pair2) 499 { 500 int cmp; 501 long id1 = (Long)pair1[1]; 502 long id2 = (Long)pair2[1]; 503 cmp = (id1 < id2 ? -1 : (id1 > id2 ? 1 : 0)); 504 if (cmp != 0) { 505 return cmp; 506 } 507 508 AggregationRecord r1 = (AggregationRecord)pair1[0]; 509 AggregationRecord r2 = (AggregationRecord)pair2[0]; 510 AggregationValue v1 = r1.getValue(); 511 AggregationValue v2 = r2.getValue(); 512 cmp = compareValues(v1, v2); 513 if (cmp != 0) { 514 return cmp; 515 } 516 517 cmp = compareTuples(r1.getTuple(), r2.getTuple(), keypos); 518 return cmp; 519 } 520 }; 521 return CMP; 522 }; 523 524 static Comparator <Object[]> 525 getAggKeyCmp(final int keypos) 526 { 527 Comparator <Object[]> CMP = new Comparator <Object[]> () { 528 public int 529 compare(Object[] pair1, Object[] pair2) 530 { 531 int cmp; 532 long id1 = (Long)pair1[1]; 533 long id2 = (Long)pair2[1]; 534 cmp = (id1 < id2 ? -1 : (id1 > id2 ? 1 : 0)); 535 if (cmp != 0) { 536 return cmp; 537 } 538 539 AggregationRecord r1 = (AggregationRecord)pair1[0]; 540 AggregationRecord r2 = (AggregationRecord)pair2[0]; 541 cmp = compareTuples(r1.getTuple(), r2.getTuple(), keypos); 542 543 return cmp; 544 } 545 }; 546 return CMP; 547 } 548 549 // Consumer getAggregate() 550 static void 551 printAggregate(Aggregate aggregate) 552 { 553 List <AggregationRecord> list = 554 new ArrayList <AggregationRecord> (); 555 List <Object[]> sortList = new ArrayList <Object[]> (); 556 for (Aggregation a : aggregate.getAggregations()) { 557 for (AggregationRecord rec : a.asMap().values()) { 558 sortList.add(new Object[] {rec, new Long(a.getID())}); 559 } 560 } 561 562 try { 563 // aggsortkeypos 564 long optval = dtrace.getOption(Option.aggsortkeypos); 565 int keypos; 566 if (optval == Option.UNSET) { 567 keypos = 0; 568 } else { 569 keypos = (int)optval; 570 } 571 572 // aggsortkey 573 if (dtrace.getOption(Option.aggsortkey) == Option.UNSET) { 574 Collections.sort(sortList, getAggValCmp(keypos)); 575 } else { 576 Collections.sort(sortList, getAggKeyCmp(keypos)); 577 } 578 579 for (Object[] pair : sortList) { 580 list.add((AggregationRecord)pair[0]); 581 } 582 583 // aggsortrev 584 if (dtrace.getOption(Option.aggsortrev) != Option.UNSET) { 585 Collections.reverse(list); 586 } 587 } catch (DTraceException e) { 588 e.printStackTrace(); 589 return; 590 } 591 592 printAggregationRecords(list); 593 } 594 595 static void 596 printAggregationRecords(List <AggregationRecord> list) 597 { 598 Tuple tuple; 599 AggregationValue value; 600 ValueRecord tupleRecord; 601 int i; 602 int len; 603 for (AggregationRecord r : list) { 604 tuple = r.getTuple(); 605 value = r.getValue(); 606 len = tuple.size(); 607 for (i = 0; i < len; ++i) { 608 tupleRecord = tuple.get(i); 609 if (tupleRecord instanceof StackValueRecord) { 610 printStack((StackValueRecord)tupleRecord); 611 } else if (tupleRecord instanceof SymbolValueRecord) { 612 printValue(tupleRecord.toString(), -1, " %-50s"); 613 } else { 614 printValue(tupleRecord.getValue(), 615 ((ScalarRecord)tupleRecord).getNumberOfBytes(), 616 " %-50s"); 617 } 618 } 619 if (value instanceof Distribution) { 620 Distribution d = (Distribution)value; 621 printDistribution(d); 622 } else { 623 Number v = value.getValue(); 624 printValue(v, -1, " %-50s"); 625 } 626 out.println(); 627 } 628 } 629 630 static void 631 exit(int status) 632 { 633 out.flush(); 634 System.err.flush(); 635 if (status == 0) { 636 status = exitStatus; 637 } 638 System.exit(status); 639 } 640 641 static void 642 usage() 643 { 644 String predact = "[[ predicate ] action ]"; 645 System.err.printf("Usage: java %s [-32|-64] [-CeFlqvVwZ] " + 646 "[-b bufsz] [-c cmd] [-D name[=def]]\n\t[-I path] [-L path] " + 647 "[-o output] [-p pid] [-s script] [-U name]\n\t" + 648 "[-x opt[=val]] [-X a|c|s|t]\n\n" + 649 "\t[-P provider %s]\n" + 650 "\t[-m [ provider: ] module %s]\n" + 651 "\t[-f [[ provider: ] module: ] func %s]\n" + 652 "\t[-n [[[ provider: ] module: ] func: ] name %s]\n" + 653 "\t[-i probe-id %s] [ args ... ]\n\n", CLASSNAME, 654 predact, predact, predact, predact, predact); 655 System.err.printf("\tpredicate -> '/' D-expression '/'\n"); 656 System.err.printf("\t action -> '{' D-statements '}'\n"); 657 System.err.printf("\n" + 658 "\t-32 generate 32-bit D programs\n" + 659 "\t-64 generate 64-bit D programs\n\n" + 660 "\t-b set trace buffer size\n" + 661 "\t-c run specified command and exit upon its completion\n" + 662 "\t-C run cpp(1) preprocessor on script files\n" + 663 "\t-D define symbol when invoking preprocessor\n" + 664 "\t-e exit after compiling request but prior to enabling " + 665 "probes\n" + 666 "\t-f enable or list probes matching the specified " + 667 "function name\n" + 668 "\t-F coalesce trace output by function\n" + 669 "\t-i enable or list probes matching the specified probe id\n" + 670 "\t-I add include directory to preprocessor search path\n" + 671 "\t-l list probes matching specified criteria\n" + 672 "\t-L add library directory to library search path\n" + 673 "\t-m enable or list probes matching the specified " + 674 "module name\n" + 675 "\t-n enable or list probes matching the specified probe name\n" + 676 "\t-o set output file\n" + 677 "\t-p grab specified process-ID and cache its symbol tables\n" + 678 "\t-P enable or list probes matching the specified " + 679 "provider name\n" + 680 "\t-q set quiet mode (only output explicitly traced data)\n" + 681 "\t-s enable or list probes according to the specified " + 682 "D script\n" + 683 "\t-U undefine symbol when invoking preprocessor\n" + 684 "\t-v set verbose mode (report stability attributes, " + 685 "arguments)\n" + 686 "\t-V report DTrace API version\n" + 687 "\t-w permit destructive actions\n" + 688 "\t-x enable or modify compiler and tracing options\n" + 689 "\t-X specify ISO C conformance settings for preprocessor\n" + 690 "\t-Z permit probe descriptions that match zero probes\n" + 691 "\n" + 692 "\tTo log PrintaRecord, set this environment variable:\n" + 693 "\t\tJDTRACE_LOGGING_LEVEL=FINE\n" + 694 "\tTo log ProbeData, set JDTRACE_LOGGING_LEVEL=FINER\n"); 695 exit(2); 696 } 697 698 static void 699 printProgramStability(String programType, String programDescription, 700 ProgramInfo info) 701 { 702 out.println(); 703 out.printf("Stability data for %s %s:\n\n", 704 programType, programDescription); 705 InterfaceAttributes a; 706 out.println("\tMinimum probe description " + 707 "attributes"); 708 a = info.getMinimumProbeAttributes(); 709 out.printf("\t\tIdentifier Names: %s\n", 710 a.getNameStability()); 711 out.printf("\t\tData Semantics: %s\n", 712 a.getDataStability()); 713 out.printf("\t\tDependency Class: %s\n", 714 a.getDependencyClass()); 715 out.println("\tMinimum probe statement attributes"); 716 a = info.getMinimumStatementAttributes(); 717 out.printf("\t\tIdentifier Names: %s\n", 718 a.getNameStability()); 719 out.printf("\t\tData Semantics: %s\n", 720 a.getDataStability()); 721 out.printf("\t\tDependency Class: %s\n", 722 a.getDependencyClass()); 723 } 724 725 static void 726 printProbeDescription(ProbeDescription p) 727 { 728 out.printf("%5d %10s %17s %33s %s\n", p.getID(), 729 p.getProvider(), p.getModule(), 730 p.getFunction(), p.getName()); 731 } 732 733 static void 734 printProbeInfo(ProbeInfo p) 735 { 736 InterfaceAttributes a; 737 out.println("\n\tProbe Description Attributes"); 738 739 a = p.getProbeAttributes(); 740 out.printf("\t\tIdentifier Names: %s\n", 741 a.getNameStability()); 742 out.printf("\t\tData Semantics: %s\n", 743 a.getDataStability()); 744 out.printf("\t\tDependency Class: %s\n", 745 a.getDependencyClass()); 746 747 out.println("\n\tArgument Attributes"); 748 749 a = p.getArgumentAttributes(); 750 out.printf("\t\tIdentifier Names: %s\n", 751 a.getNameStability()); 752 out.printf("\t\tData Semantics: %s\n", 753 a.getDataStability()); 754 out.printf("\t\tDependency Class: %s\n", 755 a.getDependencyClass()); 756 757 // Argument types unsupported for now. 758 759 out.println(); 760 } 761 762 public static void 763 main(String[] args) 764 { 765 String loggingLevel = System.getenv().get("JDTRACE_LOGGING_LEVEL"); 766 try { 767 logger.setLevel(Level.parse(loggingLevel)); 768 } catch (Exception e) { 769 logger.setLevel(Level.OFF); 770 } 771 772 if (args.length == 0) { 773 usage(); 774 } 775 776 List <CompileRequest> compileRequests = new LinkedList 777 <CompileRequest> (); 778 List <Program> programList = new LinkedList <Program> (); 779 boolean verbose = false; 780 Mode mode = Mode.EXEC; 781 782 final ExceptionHandler exceptionHandler = new ExceptionHandler() { 783 public void handleException(Throwable e) { 784 if (e instanceof DTraceException) { 785 DTraceException de = (DTraceException)e; 786 System.err.printf("dtrace: %s\n", de.getMessage()); 787 } else if (e instanceof ConsumerException) { 788 ConsumerException ce = (ConsumerException)e; 789 Object msg = ce.getNotificationObject(); 790 if ((msg instanceof org.opensolaris.os.dtrace.Error) || 791 (msg instanceof Drop)) { 792 System.err.printf("dtrace: %s\n", ce.getMessage()); 793 } else { 794 ce.printStackTrace(); 795 } 796 } else { 797 e.printStackTrace(); 798 } 799 exit(1); 800 } 801 }; 802 803 Getopt g = new Getopt(CLASSNAME, args, OPTSTR); 804 int c = 0; 805 806 List <Consumer.OpenFlag> openFlags = 807 new ArrayList <Consumer.OpenFlag> (); 808 809 while ((c = g.getopt()) != -1) { 810 switch (c) { 811 case '3': { 812 String s = g.getOptarg(); 813 if (!s.equals("2")) { 814 System.err.println("dtrace: illegal option -- 3" + s); 815 usage(); 816 } 817 openFlags.add(Consumer.OpenFlag.ILP32); 818 break; 819 } 820 case '6': { 821 String s = g.getOptarg(); 822 if (!s.equals("4")) { 823 System.err.println("dtrace: illegal option -- 6" + s); 824 usage(); 825 } 826 openFlags.add(Consumer.OpenFlag.LP64); 827 break; 828 } 829 } 830 } 831 832 Consumer.OpenFlag[] oflags = new Consumer.OpenFlag[openFlags.size()]; 833 oflags = (Consumer.OpenFlag[])openFlags.toArray(oflags); 834 835 dtrace = new LocalConsumer() { 836 protected Thread createThread() { 837 Thread t = super.createThread(); 838 t.setDaemon(false); 839 t.setPriority(Thread.MIN_PRIORITY); 840 return t; 841 } 842 }; 843 844 g = new Getopt(CLASSNAME, args, OPTSTR); 845 c = 0; 846 847 try { 848 dtrace.open(oflags); 849 850 // Set default options that may be overriden by options or #pragma 851 dtrace.setOption(Option.bufsize, Option.mb(4)); 852 dtrace.setOption(Option.aggsize, Option.mb(4)); 853 854 CompileRequest r; 855 while ((c = g.getopt()) != -1) { 856 switch (c) { 857 case 'b': 858 dtrace.setOption(Option.bufsize, g.getOptarg()); 859 break; 860 case 'c': 861 dtrace.createProcess(g.getOptarg()); 862 break; 863 case 'C': 864 dtrace.setOption(Option.cpp); 865 break; 866 case 'D': 867 dtrace.setOption(Option.define, g.getOptarg()); 868 break; 869 case 'e': 870 mode = Mode.INFO; 871 break; 872 case 'f': 873 r = new CompileRequest(); 874 r.s = g.getOptarg(); 875 r.type = ProgramType.STRING; 876 r.probespec = ProbeDescription.Spec.FUNCTION; 877 compileRequests.add(r); 878 break; 879 case 'F': 880 dtrace.setOption(Option.flowindent); 881 break; 882 case 'i': 883 r = new CompileRequest(); 884 r.s = g.getOptarg(); 885 r.type = ProgramType.STRING; 886 r.probespec = ProbeDescription.Spec.NAME; 887 compileRequests.add(r); 888 break; 889 case 'I': 890 dtrace.setOption(Option.incdir, g.getOptarg()); 891 break; 892 case 'l': 893 mode = Mode.LIST; 894 dtrace.setOption(Option.zdefs); // -l implies -Z 895 break; 896 case 'L': 897 dtrace.setOption(Option.libdir, g.getOptarg()); 898 break; 899 case 'm': 900 r = new CompileRequest(); 901 r.s = g.getOptarg(); 902 r.type = ProgramType.STRING; 903 r.probespec = ProbeDescription.Spec.MODULE; 904 compileRequests.add(r); 905 break; 906 case 'n': 907 r = new CompileRequest(); 908 r.s = g.getOptarg(); 909 r.type = ProgramType.STRING; 910 r.probespec = ProbeDescription.Spec.NAME; 911 compileRequests.add(r); 912 break; 913 case 'o': 914 String outFileName = g.getOptarg(); 915 File outFile = new File(outFileName); 916 try { 917 FileOutputStream fos = new FileOutputStream( 918 outFile, true); 919 out = new PrintStream(fos); 920 } catch (FileNotFoundException e) { 921 System.err.println("failed to open " + 922 outFileName + " in write mode"); 923 exit(1); 924 } catch (SecurityException e) { 925 System.err.println("failed to open " + 926 outFileName); 927 exit(1); 928 } 929 break; 930 case 'p': 931 String pidstr = g.getOptarg(); 932 int pid = -1; 933 try { 934 pid = Integer.parseInt(pidstr); 935 } catch (NumberFormatException e) { 936 System.err.println("invalid pid: " + pidstr); 937 exit(1); 938 } 939 dtrace.grabProcess(pid); 940 break; 941 case 'P': 942 r = new CompileRequest(); 943 r.s = g.getOptarg(); 944 r.type = ProgramType.STRING; 945 r.probespec = ProbeDescription.Spec.PROVIDER; 946 compileRequests.add(r); 947 break; 948 case 'q': 949 dtrace.setOption(Option.quiet); 950 break; 951 case 's': 952 r = new CompileRequest(); 953 r.s = g.getOptarg(); 954 r.type = ProgramType.FILE; 955 compileRequests.add(r); 956 break; 957 case 'U': 958 dtrace.setOption(Option.undef, g.getOptarg()); 959 break; 960 case 'v': 961 verbose = true; 962 break; 963 case 'V': 964 mode = Mode.VERSION; 965 break; 966 case 'w': 967 dtrace.setOption(Option.destructive); 968 break; 969 case 'x': 970 String[] xarg = g.getOptarg().split("=", 2); 971 if (xarg.length > 1) { 972 dtrace.setOption(xarg[0], xarg[1]); 973 } else if (xarg.length == 1) { 974 dtrace.setOption(xarg[0]); 975 } 976 break; 977 case 'X': 978 dtrace.setOption(Option.stdc, g.getOptarg()); 979 break; 980 case 'Z': 981 dtrace.setOption(Option.zdefs); 982 break; 983 case '?': 984 usage(); // getopt() already printed an error 985 break; 986 default: 987 System.err.print("getopt() returned " + c + "\n"); 988 c = 0; 989 } 990 } 991 c = 0; 992 List <String> argList = new LinkedList <String> (); 993 for (int i = g.getOptind(); i < args.length; ++i) { 994 argList.add(args[i]); 995 } 996 997 if (mode == Mode.VERSION) { 998 out.printf("dtrace: %s\n", dtrace.getVersion()); 999 dtrace.close(); 1000 exit(0); 1001 } 1002 1003 String[] compileArgs = new String[argList.size()]; 1004 compileArgs = (String[])argList.toArray(compileArgs); 1005 1006 if (compileRequests.isEmpty()) { 1007 System.err.println("dtrace: no probes specified"); 1008 exit(1); 1009 } 1010 1011 Program program; 1012 for (CompileRequest req : compileRequests) { 1013 switch (req.type) { 1014 case STRING: 1015 applyProbespec(req); 1016 program = dtrace.compile(req.s, compileArgs); 1017 break; 1018 case FILE: 1019 File file = new File(req.s); 1020 program = dtrace.compile(file, compileArgs); 1021 break; 1022 default: 1023 throw new IllegalArgumentException( 1024 "Unexpected program type: " + req.type); 1025 } 1026 1027 programList.add(program); 1028 } 1029 1030 // Get options set by #pragmas in compiled program 1031 long optval; 1032 quiet = (dtrace.getOption(Option.quiet) != Option.UNSET); 1033 flow = (dtrace.getOption(Option.flowindent) != Option.UNSET); 1034 optval = dtrace.getOption("stackindent"); 1035 if (optval != Option.UNSET) { 1036 stackindent = (int)optval; 1037 } 1038 1039 if (mode == Mode.LIST) { 1040 out.printf("%5s %10s %17s %33s %s\n", 1041 "ID", "PROVIDER", "MODULE", "FUNCTION", "NAME"); 1042 1043 if (verbose) { 1044 List <List <Probe>> lists = 1045 new LinkedList <List <Probe>> (); 1046 for (Program p : programList) { 1047 lists.add(dtrace.listProgramProbeDetail(p)); 1048 } 1049 ProbeDescription p; 1050 ProbeInfo pinfo; 1051 for (List <Probe> list : lists) { 1052 for (Probe probe : list) { 1053 p = probe.getDescription(); 1054 pinfo = probe.getInfo(); 1055 printProbeDescription(p); 1056 printProbeInfo(pinfo); 1057 } 1058 } 1059 } else { 1060 List <List <ProbeDescription>> lists = 1061 new LinkedList <List <ProbeDescription>> (); 1062 for (Program p : programList) { 1063 lists.add(dtrace.listProgramProbes(p)); 1064 } 1065 for (List <ProbeDescription> list : lists) { 1066 for (ProbeDescription p : list) { 1067 printProbeDescription(p); 1068 } 1069 } 1070 } 1071 exit(0); 1072 } 1073 1074 String programType; 1075 String programDescription; 1076 ProgramInfo info; 1077 for (Program p : programList) { 1078 if (p instanceof Program.File) { 1079 Program.File pf = (Program.File)p; 1080 programType = "script"; 1081 programDescription = pf.getFile().getPath(); 1082 } else { 1083 programType = "description"; 1084 programDescription = 1085 p.getContents().split("[/{;]", 2)[0]; 1086 } 1087 1088 if (mode == Mode.EXEC) { 1089 dtrace.enable(p); 1090 } else { 1091 dtrace.getProgramInfo(p); 1092 } 1093 info = p.getInfo(); 1094 if (!quiet) { 1095 System.err.printf("dtrace: %s '%s' matched %d probe%s\n", 1096 programType, programDescription, 1097 info.getMatchingProbeCount(), 1098 info.getMatchingProbeCount() == 1 ? "" : "s"); 1099 } 1100 if (verbose) { 1101 printProgramStability(programType, 1102 programDescription, info); 1103 } 1104 } 1105 if (mode != Mode.EXEC) { 1106 exit(0); 1107 } 1108 dtrace.addConsumerListener(new ConsumerAdapter() { 1109 public void consumerStarted(ConsumerEvent e) { 1110 started = true; 1111 } 1112 public void consumerStopped(ConsumerEvent e) { 1113 stopped = true; 1114 out.println(); 1115 try { 1116 Aggregate aggregate = dtrace.getAggregate(); 1117 if (aggregate != null) { 1118 printAggregate(aggregate); 1119 } 1120 dtrace.close(); 1121 } catch (Throwable x) { 1122 exceptionHandler.handleException(x); 1123 } 1124 exit(0); 1125 } 1126 public void dataDropped(DropEvent e) { 1127 System.err.printf("dtrace: %s", 1128 e.getDrop().getDefaultMessage()); 1129 } 1130 public void errorEncountered(ErrorEvent e) 1131 throws ConsumerException { 1132 org.opensolaris.os.dtrace.Error error = e.getError(); 1133 if (logger.isLoggable(Level.FINE)) { 1134 logger.fine(error.toString()); 1135 } 1136 System.err.printf("dtrace: %s", 1137 error.getDefaultMessage()); 1138 } 1139 public void dataReceived(DataEvent e) 1140 throws ConsumerException { 1141 consumeProbeData(e.getProbeData()); 1142 } 1143 public void processStateChanged(ProcessEvent e) 1144 throws ConsumerException { 1145 if (logger.isLoggable(Level.FINE)) { 1146 logger.fine(e.getProcessState().toString()); 1147 } 1148 } 1149 }); 1150 // Print unprinted aggregations after Ctrl-C 1151 Runtime.getRuntime().addShutdownHook(new Thread() { 1152 public void run() { 1153 if (stopped || !started) { 1154 return; 1155 } 1156 1157 try { 1158 Aggregate aggregate = dtrace.getAggregate(); 1159 if (aggregate != null) { 1160 out.println(); 1161 out.println(); 1162 printAggregate(aggregate); 1163 } 1164 } catch (Throwable x) { 1165 exceptionHandler.handleException(x); 1166 } 1167 } 1168 }); 1169 dtrace.go(exceptionHandler); 1170 } catch (DTraceException e) { 1171 if (c > 0) { 1172 // set option error 1173 if (g.getOptarg() == null) { 1174 System.err.printf("dtrace: failed to set -%c: %s\n", 1175 c, e.getMessage()); 1176 } else { 1177 System.err.printf("dtrace: failed to set -%c %s: %s\n", 1178 c, g.getOptarg(), e.getMessage()); 1179 } 1180 } else { 1181 // any other error 1182 System.err.printf("dtrace: %s\n", e.getMessage()); 1183 } 1184 exit(1); 1185 } catch (Exception e) { 1186 e.printStackTrace(); 1187 exit(1); 1188 } 1189 } 1190 } 1191