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 package org.opensolaris.os.dtrace; 29 30 import java.io.*; 31 import java.util.*; 32 import java.net.InetAddress; 33 import java.net.UnknownHostException; 34 import javax.swing.event.EventListenerList; 35 import java.util.logging.*; 36 37 /** 38 * Interface to the native DTrace library, each instance is a single 39 * DTrace consumer. 40 * 41 * @author Tom Erickson 42 */ 43 public class LocalConsumer implements Consumer { 44 // 45 // Implementation notes: 46 // 47 // libdtrace is *not* thread-safe. You cannot make multiple calls 48 // into it simultaneously from different threads, even if those 49 // threads are operating on different dtrace_hdl_t's. Calls to 50 // libdtrace are synchronized on a global lock, LocalConsumer.class. 51 52 static Logger logger = Logger.getLogger(LocalConsumer.class.getName()); 53 54 private static final int DTRACE_JNI_VERSION = 1; 55 56 private static final Option[] DEFAULT_OPTIONS = new Option[] { 57 new Option(Option.bufsize, Option.kb(256)), 58 new Option(Option.aggsize, Option.kb(256)), 59 }; 60 61 private static native void _loadJniTable(); 62 63 private static boolean debug; 64 private static int maxConsumers; 65 66 static { 67 // configure logging 68 logger.addHandler(new ConsoleHandler()); 69 logger.setLevel(Level.OFF); 70 71 // Undocumented configuration options using java -Doption=value 72 String property; 73 property = System.getProperty("JAVA_DTRACE_MAX_CONSUMERS"); 74 if (property != null && property.length() != 0) { 75 try { 76 maxConsumers = Integer.parseInt(property); 77 } catch (NumberFormatException e) { 78 e.printStackTrace(); 79 System.exit(1); 80 } 81 } 82 property = System.getProperty("JAVA_DTRACE_API_DEBUG"); 83 if (property != null && property.length() != 0) { 84 try { 85 int i = Integer.parseInt(property); 86 debug = (i != 0); 87 } catch (NumberFormatException e) { 88 System.err.println("Warning: property ignored: " + 89 "JAVA_DTRACE_API_DEBUG=" + property); 90 } 91 } 92 93 Utility.loadLibrary("libdtrace_jni.so.1", debug); 94 95 _checkVersion(DTRACE_JNI_VERSION); 96 if (maxConsumers > 0) { 97 _setMaximumConsumers(maxConsumers); 98 } 99 _setDebug(debug); 100 101 // 102 // Last of all in case configuration options affect the loading 103 // of the JNI table. 104 // 105 _loadJniTable(); 106 } 107 108 // Native JNI interface (see lib/libdtrace_jni/dtrace_jni.c) 109 private static native void _checkVersion(int version); 110 private native void _open(OpenFlag[] flags) throws DTraceException; 111 private native Program _compileString(String program, String[] args) 112 throws DTraceException; 113 private native Program.File _compileFile(String path, String[] args) 114 throws DTraceException; 115 private native void _exec(Program program) throws DTraceException; 116 private native void _getProgramInfo(Program program) 117 throws DTraceException; 118 private native void _setOption(String option, String value) 119 throws DTraceException; 120 private native long _getOption(String option) throws DTraceException; 121 private native boolean _isEnabled(); 122 private native void _checkProgramEnabling(); 123 private native void _go() throws DTraceException; 124 private native void _stop() throws DTraceException; 125 private native void _consume() throws DTraceException; 126 private native void _interrupt(); 127 private native void _close(); 128 private native Aggregate _getAggregate(AggregateSpec spec) 129 throws DTraceException; 130 private native int _createProcess(String cmd) throws DTraceException; 131 private native void _grabProcess(int pid) throws DTraceException; 132 private native void _listProbes(List <ProbeDescription> probeList, 133 ProbeDescription filter); 134 private native void _listProbeDetail(List <Probe> probeList, 135 ProbeDescription filter); 136 private native void _listCompiledProbes( 137 List <ProbeDescription> probeList, Program program); 138 private native void _listCompiledProbeDetail( 139 List <Probe> probeList, Program program); 140 private static native String _getVersion(); 141 private static native int _openCount(); 142 // 143 // Releases memory held in the JNI layer after dtrace_close() has 144 // released critical system resources like file descriptors, and 145 // calls to libdtrace are no longer needed (or possible). 146 // 147 private native void _destroy(); 148 // Called by LogDistribution 149 static native long _quantizeBucket(int i); 150 // 151 // Cannot be static because the necessary dtrace handle is specific 152 // to this Consumer. 153 // 154 private native String _lookupKernelFunction(Number address); 155 private native String _lookupUserFunction(int pid, Number address); 156 private static native String _getExecutableName(); 157 158 // Undocumented configuration options 159 private static native void _setMaximumConsumers(int max); 160 private static native void _setDebug(boolean debug); 161 162 protected EventListenerList listenerList; 163 protected ExceptionHandler exceptionHandler; 164 165 private int _handle = -1; // native C identifier (do not modify) 166 private final Identifier id; // java identifier 167 168 private enum State { 169 INIT, 170 OPEN, 171 COMPILED, 172 GO, 173 STARTED, 174 STOPPED, 175 CLOSED 176 } 177 178 private State state = State.INIT; 179 private boolean stopCalled; 180 181 // 182 // Per-consumer lock used in native code to prevent conflict between 183 // the native consumer loop and the getAggregate() thread without 184 // locking this LocalConsumer. A distinct per-consumer lock allows 185 // the stop() method to be synchronized without causing deadlock 186 // when the consumer loop grabs the per-consumer lock before 187 // dtrace_work(). 188 // 189 private Object consumerLock; 190 // 191 // Synchronization lock used to ensure that stop() does not return 192 // until this consumer is actually stopped. 193 // 194 private Object stopLock; 195 private boolean workEnded; 196 197 private static int sequence = 0; 198 199 /** 200 * Creates a consumer that interacts with the native DTrace library 201 * on the local system. 202 */ 203 public 204 LocalConsumer() 205 { 206 id = new LocalConsumer.Identifier(this); 207 consumerLock = new Object(); 208 stopLock = new Object(); 209 listenerList = new EventListenerList(); 210 } 211 212 /** 213 * Called by native C code only 214 */ 215 private int 216 getHandle() 217 { 218 return _handle; 219 } 220 221 /** 222 * Called by native C code only 223 */ 224 private void 225 setHandle(int n) 226 { 227 _handle = n; 228 } 229 230 public synchronized void 231 open(OpenFlag ... flags) throws DTraceException 232 { 233 if (state == State.CLOSED) { 234 throw new IllegalStateException("cannot reopen a closed consumer"); 235 } 236 if (state != State.INIT) { 237 throw new IllegalStateException("consumer already open"); 238 } 239 240 for (OpenFlag flag : flags) { 241 if (flag == null) { 242 throw new NullPointerException("open flag is null"); 243 } 244 } 245 246 synchronized (LocalConsumer.class) { 247 _open(flags); 248 } 249 250 state = State.OPEN; 251 setOptions(DEFAULT_OPTIONS); 252 253 if (logger.isLoggable(Level.INFO)) { 254 logger.info("consumer table count: " + _openCount()); 255 } 256 } 257 258 private synchronized void 259 checkCompile() 260 { 261 switch (state) { 262 case INIT: 263 throw new IllegalStateException("consumer not open"); 264 case OPEN: 265 case COMPILED: // caller may compile more than one program 266 break; 267 case GO: 268 case STARTED: 269 throw new IllegalStateException("go() already called"); 270 case STOPPED: 271 throw new IllegalStateException("consumer stopped"); 272 case CLOSED: 273 throw new IllegalStateException("consumer closed"); 274 } 275 } 276 277 public synchronized Program 278 compile(String program, String ... macroArgs) throws DTraceException 279 { 280 if (program == null) { 281 throw new NullPointerException("program string is null"); 282 } 283 checkCompile(); 284 Program p = null; 285 286 String[] argv = null; 287 if (macroArgs != null) { 288 for (String macroArg : macroArgs) { 289 if (macroArg == null) { 290 throw new NullPointerException("macro argument is null"); 291 } 292 } 293 argv = new String[macroArgs.length + 1]; 294 synchronized (LocalConsumer.class) { 295 // 296 // Could be an application with an embedded JVM, not 297 // necessarily "java". 298 // 299 argv[0] = _getExecutableName(); 300 } 301 System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); 302 } else { 303 synchronized (LocalConsumer.class) { 304 argv = new String[] { _getExecutableName() }; 305 } 306 } 307 synchronized (LocalConsumer.class) { 308 p = _compileString(program, argv); 309 } 310 p.consumerID = id; 311 p.contents = program; 312 p.validate(); 313 state = State.COMPILED; 314 315 return p; 316 } 317 318 public synchronized Program 319 compile(File program, String ... macroArgs) throws DTraceException, 320 IOException, SecurityException 321 { 322 if (program == null) { 323 throw new NullPointerException("program file is null"); 324 } 325 if (!program.canRead()) { 326 throw new FileNotFoundException("failed to open " + 327 program.getName()); 328 } 329 checkCompile(); 330 Program.File p = null; 331 332 String[] argv = null; 333 if (macroArgs != null) { 334 for (String macroArg : macroArgs) { 335 if (macroArg == null) { 336 throw new NullPointerException("macro argument is null"); 337 } 338 } 339 argv = new String[macroArgs.length + 1]; 340 argv[0] = program.getPath(); 341 System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); 342 } else { 343 macroArgs = new String[] { program.getPath() }; 344 } 345 synchronized (LocalConsumer.class) { 346 p = _compileFile(program.getPath(), argv); 347 } 348 p.consumerID = id; 349 p.contents = Program.getProgramString(program); 350 p.file = program; 351 p.validate(); 352 state = State.COMPILED; 353 354 return p; 355 } 356 357 private synchronized void 358 checkProgram(Program program) 359 { 360 if (program == null) { 361 throw new NullPointerException("program is null"); 362 } 363 if (!id.equals(program.consumerID)) { 364 throw new IllegalArgumentException("program not compiled " + 365 "by this consumer"); 366 } 367 } 368 369 public void 370 enable() throws DTraceException 371 { 372 enable(null); 373 } 374 375 public synchronized void 376 enable(Program program) throws DTraceException 377 { 378 switch (state) { 379 case INIT: 380 throw new IllegalStateException("consumer not open"); 381 case OPEN: 382 throw new IllegalStateException("no compiled program"); 383 case COMPILED: 384 break; 385 case GO: 386 case STARTED: 387 throw new IllegalStateException("go() already called"); 388 case STOPPED: 389 throw new IllegalStateException("consumer stopped"); 390 case CLOSED: 391 throw new IllegalStateException("consumer closed"); 392 } 393 394 // Compile all programs if null 395 if (program != null) { 396 checkProgram(program); 397 } 398 399 // 400 // Left to native code to throw IllegalArgumentException if the 401 // program is already enabled, since only the native code knows 402 // the enabled state. 403 // 404 synchronized (LocalConsumer.class) { 405 _exec(program); 406 } 407 } 408 409 public synchronized void 410 getProgramInfo(Program program) throws DTraceException 411 { 412 checkProgram(program); 413 if (state == State.CLOSED) { 414 throw new IllegalStateException("consumer closed"); 415 } 416 417 // 418 // The given program was compiled by this consumer, so we can 419 // assert the following: 420 // 421 assert ((state != State.INIT) && (state != State.OPEN)); 422 423 synchronized (LocalConsumer.class) { 424 _getProgramInfo(program); 425 } 426 } 427 428 private void 429 setOptions(Option[] options) throws DTraceException 430 { 431 for (Option o : options) { 432 setOption(o.getOption(), o.getValue()); 433 } 434 } 435 436 public void 437 setOption(String option) throws DTraceException 438 { 439 setOption(option, Option.VALUE_SET); 440 } 441 442 public void 443 unsetOption(String option) throws DTraceException 444 { 445 setOption(option, Option.VALUE_UNSET); 446 } 447 448 public synchronized void 449 setOption(String option, String value) throws DTraceException 450 { 451 if (option == null) { 452 throw new NullPointerException("option is null"); 453 } 454 if (value == null) { 455 throw new NullPointerException("option value is null"); 456 } 457 458 switch (state) { 459 case INIT: 460 throw new IllegalStateException("consumer not open"); 461 case OPEN: 462 case COMPILED: 463 case GO: 464 case STARTED: // Some options can be set on a running consumer 465 case STOPPED: // Allowed (may affect getAggregate()) 466 break; 467 case CLOSED: 468 throw new IllegalStateException("consumer closed"); 469 } 470 471 synchronized (LocalConsumer.class) { 472 _setOption(option, value); 473 } 474 } 475 476 public synchronized long 477 getOption(String option) throws DTraceException 478 { 479 if (option == null) { 480 throw new NullPointerException("option is null"); 481 } 482 483 switch (state) { 484 case INIT: 485 throw new IllegalStateException("consumer not open"); 486 case OPEN: 487 case COMPILED: 488 case GO: 489 case STARTED: 490 case STOPPED: 491 break; 492 case CLOSED: 493 throw new IllegalStateException("consumer closed"); 494 } 495 496 long value; 497 synchronized (LocalConsumer.class) { 498 value = _getOption(option); 499 } 500 return value; 501 } 502 503 public final synchronized boolean 504 isOpen() 505 { 506 return ((state != State.INIT) && (state != State.CLOSED)); 507 } 508 509 public final synchronized boolean 510 isEnabled() 511 { 512 if (state != State.COMPILED) { 513 return false; 514 } 515 516 return _isEnabled(); 517 } 518 519 public final synchronized boolean 520 isRunning() 521 { 522 return (state == State.STARTED); 523 } 524 525 public final synchronized boolean 526 isClosed() 527 { 528 return (state == State.CLOSED); 529 } 530 531 /** 532 * Called in the runnable target of the thread returned by {@link 533 * #createThread()} to run this DTrace consumer. 534 * 535 * @see #createThread() 536 */ 537 protected final void 538 work() 539 { 540 try { 541 synchronized (this) { 542 if (state != State.GO) { 543 // 544 // stop() was called after go() but before the 545 // consumer started 546 // 547 return; // executes finally block before returning 548 } 549 550 state = State.STARTED; 551 fireConsumerStarted(new ConsumerEvent(this, 552 System.nanoTime())); 553 } 554 555 // 556 // We should not prevent other consumers from running 557 // concurrently while this consumer blocks on the native 558 // consumer loop. Instead, native code will acquire the 559 // LocalConsumer.class monitor as needed before calling 560 // libdtrace functions. 561 // 562 _consume(); 563 564 } catch (Throwable e) { 565 if (exceptionHandler != null) { 566 exceptionHandler.handleException(e); 567 } else { 568 e.printStackTrace(); 569 } 570 } finally { 571 synchronized (stopLock) { 572 // Notify the stop() method to stop waiting 573 workEnded = true; 574 stopLock.notifyAll(); 575 } 576 577 // Waits for stop() to finish 578 synchronized (this) { 579 // 580 // The stop() method may have updated the state and 581 // notified listeners already. Note that whoever 582 // updates the state and notifies listeners must be 583 // holding the lock on this LocalConsumer. If we update 584 // the state and notify listeners here while stop() is 585 // waiting, counting on stop() to hold the lock for us 586 // in the meantime, then a ConsumerListener could 587 // deadlock the application by calling a synchronized 588 // LocalConsumer method. 589 // 590 if (state == State.STARTED) { 591 state = State.STOPPED; 592 fireConsumerStopped(new ConsumerEvent(this, 593 System.nanoTime())); 594 } 595 } 596 } 597 } 598 599 /** 600 * Creates the background thread started by {@link #go()} to run 601 * this consumer. Override this method if you need to set 602 * non-default {@code Thread} options or create the thread in a 603 * {@code ThreadGroup}. If you don't need to create the thread 604 * yourself, set the desired options on {@code super.createThread()} 605 * before returning it. Otherwise, the {@code Runnable} target of 606 * the created thread must call {@link #work()} in order to run this 607 * DTrace consumer. For example, to modify the default background 608 * consumer thread: 609 * <pre><code> 610 * protected Thread 611 * createThread() 612 * { 613 * Thread t = super.createThread(); 614 * t.setPriority(Thread.MIN_PRIORITY); 615 * return t; 616 * } 617 * </code></pre> 618 * Or if you need to create your own thread: 619 * <pre></code> 620 * protected Thread 621 * createThread() 622 * { 623 * Runnable target = new Runnable() { 624 * public void run() { 625 * work(); 626 * } 627 * }; 628 * String name = "Consumer " + UserApplication.sequence++; 629 * Thread t = new Thread(UserApplication.threadGroup, 630 * target, name); 631 * return t; 632 * } 633 * </code></pre> 634 * Do not start the returned thread, otherwise {@code go()} will 635 * throw an {@link IllegalThreadStateException} when it tries to 636 * start the returned thread a second time. 637 */ 638 protected Thread 639 createThread() 640 { 641 Thread t = new Thread(new Runnable() { 642 public void run() { 643 work(); 644 } 645 }, "DTrace consumer " + id); 646 return t; 647 } 648 649 /** 650 * @inheritDoc 651 * @throws IllegalThreadStateException if a subclass calls {@link 652 * Thread#start()} on the value of {@link #createThread()} 653 * @see #createThread() 654 */ 655 public void 656 go() throws DTraceException 657 { 658 go(null); 659 } 660 661 /** 662 * @inheritDoc 663 * @throws IllegalThreadStateException if a subclass calls {@link 664 * Thread#start()} on the value of {@link #createThread()} 665 * @see #createThread() 666 */ 667 public synchronized void 668 go(ExceptionHandler h) throws DTraceException 669 { 670 switch (state) { 671 case INIT: 672 throw new IllegalStateException("consumer not open"); 673 case OPEN: 674 throw new IllegalStateException("no compiled program"); 675 case COMPILED: 676 // 677 // Throws IllegalStateException if not all compiled programs are 678 // also enabled. Does not make any calls to libdtrace. 679 // 680 _checkProgramEnabling(); 681 break; 682 case GO: 683 case STARTED: 684 throw new IllegalStateException("go() already called"); 685 case STOPPED: 686 throw new IllegalStateException("consumer stopped"); 687 case CLOSED: 688 throw new IllegalStateException("consumer closed"); 689 default: 690 throw new IllegalArgumentException("unknown state: " + state); 691 } 692 693 synchronized (LocalConsumer.class) { 694 _go(); 695 } 696 697 state = State.GO; 698 exceptionHandler = h; 699 Thread t = createThread(); 700 t.start(); 701 } 702 703 public synchronized void 704 stop() 705 { 706 switch (state) { 707 case INIT: 708 throw new IllegalStateException("consumer not open"); 709 case OPEN: 710 case COMPILED: 711 throw new IllegalStateException("go() not called"); 712 case GO: 713 try { 714 synchronized (LocalConsumer.class) { 715 _stop(); 716 } 717 state = State.STOPPED; 718 } catch (DTraceException e) { 719 if (exceptionHandler != null) { 720 exceptionHandler.handleException(e); 721 } else { 722 e.printStackTrace(); 723 } 724 } 725 break; 726 case STARTED: 727 // 728 // Calls no libdtrace methods, so no synchronization is 729 // needed. Sets a native flag that causes the consumer 730 // thread to exit the consumer loop and call native 731 // dtrace_stop() at the end of the current interval 732 // (after grabbing the global Consumer.class lock 733 // required for any libdtrace call). 734 // 735 _interrupt(); 736 737 synchronized (stopLock) { 738 // 739 // If the work() thread got the stopLock first, then 740 // we have nothing to wait for. (Calling wait() on 741 // the stopLock can only deadlock this LocalConsumer 742 // in that case.) However, we still need to notify 743 // listeners here, since we're holding the lock on 744 // this LocalConsumer. 745 // 746 while (!workEnded) { 747 try { 748 stopLock.wait(); 749 } catch (InterruptedException e) { 750 logger.warning(e.toString()); 751 // do nothing but re-check the condition for waiting 752 } 753 } 754 } 755 756 state = State.STOPPED; 757 fireConsumerStopped(new ConsumerEvent(this, 758 System.nanoTime())); 759 break; 760 case STOPPED: 761 // 762 // The work() thread that runs the native consumer loop 763 // may have terminated because of the exit() action in a 764 // DTrace program. In that case, a RuntimeException is 765 // inappropriate because there is no misuse of the API 766 // Creating a new checked exception type to handle this 767 // case seems to offer no benefit for the trouble to the 768 // caller. Instead, the situation calls for stop() to 769 // be quietly tolerant. 770 // 771 if (stopCalled) { 772 throw new IllegalStateException("consumer already stopped"); 773 } 774 logger.info("consumer already stopped"); 775 break; 776 case CLOSED: 777 throw new IllegalStateException("consumer closed"); 778 default: 779 throw new IllegalArgumentException("unknown state: " + state); 780 } 781 782 stopCalled = true; 783 } 784 785 public synchronized void 786 close() 787 { 788 if ((state == State.INIT) || (state == State.CLOSED)) { 789 state = State.CLOSED; 790 return; 791 } 792 793 if ((state == State.STARTED) || (state == State.GO)) { 794 stop(); 795 } 796 797 synchronized (LocalConsumer.class) { 798 _close(); 799 } 800 _destroy(); 801 state = State.CLOSED; 802 803 if (logger.isLoggable(Level.INFO)) { 804 logger.info("consumer table count: " + _openCount()); 805 } 806 } 807 808 public void 809 addConsumerListener(ConsumerListener l) 810 { 811 listenerList.add(ConsumerListener.class, l); 812 } 813 814 public void 815 removeConsumerListener(ConsumerListener l) 816 { 817 listenerList.remove(ConsumerListener.class, l); 818 } 819 820 public Aggregate 821 getAggregate() throws DTraceException 822 { 823 // include all, clear none 824 return getAggregate(null, Collections. <String> emptySet()); 825 } 826 827 public Aggregate 828 getAggregate(Set <String> includedAggregationNames) 829 throws DTraceException 830 { 831 return getAggregate(includedAggregationNames, 832 Collections. <String> emptySet()); 833 } 834 835 public Aggregate 836 getAggregate(Set <String> includedAggregationNames, 837 Set <String> clearedAggregationNames) 838 throws DTraceException 839 { 840 AggregateSpec spec = new AggregateSpec(); 841 842 if (includedAggregationNames == null) { 843 spec.setIncludeByDefault(true); 844 } else { 845 spec.setIncludeByDefault(false); 846 for (String included : includedAggregationNames) { 847 spec.addIncludedAggregationName(included); 848 } 849 } 850 851 if (clearedAggregationNames == null) { 852 spec.setClearByDefault(true); 853 } else { 854 spec.setClearByDefault(false); 855 for (String cleared : clearedAggregationNames) { 856 spec.addClearedAggregationName(cleared); 857 } 858 } 859 860 return getAggregate(spec); 861 } 862 863 private synchronized Aggregate 864 getAggregate(AggregateSpec spec) throws DTraceException 865 { 866 // 867 // It should be possible to request aggregation data after a 868 // consumer has stopped but not after it has been closed. 869 // 870 checkGoCalled(); 871 872 // 873 // Getting the aggregate is a time-consuming request that should not 874 // prevent other consumers from running concurrently. Instead, 875 // native code will acquire the LocalConsumer.class monitor as 876 // needed before calling libdtrace functions. 877 // 878 Aggregate aggregate = _getAggregate(spec); 879 return aggregate; 880 } 881 882 private synchronized void 883 checkGoCalled() 884 { 885 switch (state) { 886 case INIT: 887 throw new IllegalStateException("consumer not open"); 888 case OPEN: 889 case COMPILED: 890 throw new IllegalStateException("go() not called"); 891 case GO: 892 case STARTED: 893 case STOPPED: 894 break; 895 case CLOSED: 896 throw new IllegalStateException("consumer closed"); 897 } 898 } 899 900 private synchronized void 901 checkGoNotCalled() 902 { 903 switch (state) { 904 case INIT: 905 throw new IllegalStateException("consumer not open"); 906 case OPEN: 907 case COMPILED: 908 break; 909 case GO: 910 case STARTED: 911 throw new IllegalStateException("go() already called"); 912 case STOPPED: 913 throw new IllegalStateException("consumer stopped"); 914 case CLOSED: 915 throw new IllegalStateException("consumer closed"); 916 } 917 } 918 919 public synchronized int 920 createProcess(String command) throws DTraceException 921 { 922 if (command == null) { 923 throw new NullPointerException("command is null"); 924 } 925 926 checkGoNotCalled(); 927 928 int pid; 929 synchronized (LocalConsumer.class) { 930 pid = _createProcess(command); 931 } 932 return pid; 933 } 934 935 public synchronized void 936 grabProcess(int pid) throws DTraceException 937 { 938 checkGoNotCalled(); 939 940 synchronized (LocalConsumer.class) { 941 _grabProcess(pid); 942 } 943 } 944 945 public synchronized List <ProbeDescription> 946 listProbes(ProbeDescription filter) throws DTraceException 947 { 948 checkGoNotCalled(); 949 List <ProbeDescription> probeList = 950 new LinkedList <ProbeDescription> (); 951 if (filter == ProbeDescription.EMPTY) { 952 filter = null; 953 } 954 synchronized (LocalConsumer.class) { 955 _listProbes(probeList, filter); 956 } 957 return probeList; 958 } 959 960 public synchronized List <Probe> 961 listProbeDetail(ProbeDescription filter) throws DTraceException 962 { 963 checkGoNotCalled(); 964 List <Probe> probeList = new LinkedList <Probe> (); 965 if (filter == ProbeDescription.EMPTY) { 966 filter = null; 967 } 968 synchronized (LocalConsumer.class) { 969 _listProbeDetail(probeList, filter); 970 } 971 return probeList; 972 } 973 974 public synchronized List <ProbeDescription> 975 listProgramProbes(Program program) throws DTraceException 976 { 977 checkProgram(program); 978 checkGoNotCalled(); 979 List <ProbeDescription> probeList = 980 new LinkedList <ProbeDescription> (); 981 synchronized (LocalConsumer.class) { 982 _listCompiledProbes(probeList, program); 983 } 984 return probeList; 985 } 986 987 public synchronized List <Probe> 988 listProgramProbeDetail(Program program) throws DTraceException 989 { 990 checkProgram(program); 991 checkGoNotCalled(); 992 List <Probe> probeList = new LinkedList <Probe> (); 993 synchronized (LocalConsumer.class) { 994 _listCompiledProbeDetail(probeList, program); 995 } 996 return probeList; 997 } 998 999 public synchronized String 1000 lookupKernelFunction(int address) 1001 { 1002 checkGoCalled(); 1003 synchronized (LocalConsumer.class) { 1004 return _lookupKernelFunction(new Integer(address)); 1005 } 1006 } 1007 1008 public synchronized String 1009 lookupKernelFunction(long address) 1010 { 1011 checkGoCalled(); 1012 synchronized (LocalConsumer.class) { 1013 return _lookupKernelFunction(new Long(address)); 1014 } 1015 } 1016 1017 public synchronized String 1018 lookupUserFunction(int pid, int address) 1019 { 1020 checkGoCalled(); 1021 synchronized (LocalConsumer.class) { 1022 return _lookupUserFunction(pid, new Integer(address)); 1023 } 1024 } 1025 1026 public synchronized String 1027 lookupUserFunction(int pid, long address) 1028 { 1029 checkGoCalled(); 1030 synchronized (LocalConsumer.class) { 1031 return _lookupUserFunction(pid, new Long(address)); 1032 } 1033 } 1034 1035 public String 1036 getVersion() 1037 { 1038 synchronized (LocalConsumer.class) { 1039 return LocalConsumer._getVersion(); 1040 } 1041 } 1042 1043 /** 1044 * Called by native code. 1045 */ 1046 private void 1047 nextProbeData(ProbeData probeData) throws ConsumerException 1048 { 1049 fireDataReceived(new DataEvent(this, probeData)); 1050 } 1051 1052 /** 1053 * Called by native code. 1054 */ 1055 private void 1056 dataDropped(Drop drop) throws ConsumerException 1057 { 1058 fireDataDropped(new DropEvent(this, drop)); 1059 } 1060 1061 /** 1062 * Called by native code. 1063 */ 1064 private void 1065 errorEncountered(Error error) throws ConsumerException 1066 { 1067 fireErrorEncountered(new ErrorEvent(this, error)); 1068 } 1069 1070 /** 1071 * Called by native code. 1072 */ 1073 private void 1074 processStateChanged(ProcessState processState) throws ConsumerException 1075 { 1076 fireProcessStateChanged(new ProcessEvent(this, processState)); 1077 } 1078 1079 protected void 1080 fireDataReceived(DataEvent e) throws ConsumerException 1081 { 1082 // Guaranteed to return a non-null array 1083 Object[] listeners = listenerList.getListenerList(); 1084 // Process the listeners last to first, notifying 1085 // those that are interested in this event 1086 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1087 if (listeners[i] == ConsumerListener.class) { 1088 ((ConsumerListener)listeners[i + 1]).dataReceived(e); 1089 } 1090 } 1091 } 1092 1093 protected void 1094 fireDataDropped(DropEvent e) throws ConsumerException 1095 { 1096 // Guaranteed to return a non-null array 1097 Object[] listeners = listenerList.getListenerList(); 1098 // Process the listeners last to first, notifying 1099 // those that are interested in this event 1100 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1101 if (listeners[i] == ConsumerListener.class) { 1102 ((ConsumerListener)listeners[i + 1]).dataDropped(e); 1103 } 1104 } 1105 } 1106 1107 protected void 1108 fireErrorEncountered(ErrorEvent e) throws ConsumerException 1109 { 1110 // Guaranteed to return a non-null array 1111 Object[] listeners = listenerList.getListenerList(); 1112 // Process the listeners last to first, notifying 1113 // those that are interested in this event 1114 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1115 if (listeners[i] == ConsumerListener.class) { 1116 ((ConsumerListener)listeners[i + 1]).errorEncountered(e); 1117 } 1118 } 1119 } 1120 1121 protected void 1122 fireProcessStateChanged(ProcessEvent e) throws ConsumerException 1123 { 1124 // Guaranteed to return a non-null array 1125 Object[] listeners = listenerList.getListenerList(); 1126 // Process the listeners last to first, notifying 1127 // those that are interested in this event 1128 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1129 if (listeners[i] == ConsumerListener.class) { 1130 ((ConsumerListener)listeners[i + 1]).processStateChanged(e); 1131 } 1132 } 1133 } 1134 1135 protected void 1136 fireConsumerStarted(ConsumerEvent e) 1137 { 1138 // Guaranteed to return a non-null array 1139 Object[] listeners = listenerList.getListenerList(); 1140 // Process the listeners last to first, notifying 1141 // those that are interested in this event 1142 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1143 if (listeners[i] == ConsumerListener.class) { 1144 ((ConsumerListener)listeners[i + 1]).consumerStarted(e); 1145 } 1146 } 1147 } 1148 1149 protected void 1150 fireConsumerStopped(ConsumerEvent e) 1151 { 1152 // Guaranteed to return a non-null array 1153 Object[] listeners = listenerList.getListenerList(); 1154 // Process the listeners last to first, notifying 1155 // those that are interested in this event 1156 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1157 if (listeners[i] == ConsumerListener.class) { 1158 ((ConsumerListener)listeners[i + 1]).consumerStopped(e); 1159 } 1160 } 1161 } 1162 1163 // Called by native code 1164 private void 1165 intervalBegan() 1166 { 1167 fireIntervalBegan(new ConsumerEvent(this, System.nanoTime())); 1168 } 1169 1170 protected void 1171 fireIntervalBegan(ConsumerEvent e) 1172 { 1173 // Guaranteed to return a non-null array 1174 Object[] listeners = listenerList.getListenerList(); 1175 // Process the listeners last to first, notifying 1176 // those that are interested in this event 1177 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1178 if (listeners[i] == ConsumerListener.class) { 1179 ((ConsumerListener)listeners[i + 1]).intervalBegan(e); 1180 } 1181 } 1182 } 1183 1184 // Called by native code 1185 private void 1186 intervalEnded() 1187 { 1188 fireIntervalEnded(new ConsumerEvent(this, System.nanoTime())); 1189 } 1190 1191 protected void 1192 fireIntervalEnded(ConsumerEvent e) 1193 { 1194 // Guaranteed to return a non-null array 1195 Object[] listeners = listenerList.getListenerList(); 1196 // Process the listeners last to first, notifying 1197 // those that are interested in this event 1198 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1199 if (listeners[i] == ConsumerListener.class) { 1200 ((ConsumerListener)listeners[i + 1]).intervalEnded(e); 1201 } 1202 } 1203 } 1204 1205 /** 1206 * Gets a string representation of this consumer useful for logging 1207 * and not intended for display. The exact details of the 1208 * representation are unspecified and subject to change, but the 1209 * following format may be regarded as typical: 1210 * <pre><code> 1211 * class-name[property1 = value1, property2 = value2] 1212 * </code></pre> 1213 */ 1214 public String 1215 toString() 1216 { 1217 StringBuffer buf = new StringBuffer(LocalConsumer.class.getName()); 1218 synchronized (this) { 1219 buf.append("[open = "); 1220 buf.append(isOpen()); 1221 buf.append(", enabled = "); 1222 buf.append(isEnabled()); 1223 buf.append(", running = "); 1224 buf.append(isRunning()); 1225 buf.append(", closed = "); 1226 buf.append(isClosed()); 1227 } 1228 buf.append(']'); 1229 return buf.toString(); 1230 } 1231 1232 /** 1233 * Ensures that the {@link #close()} method of this consumer has 1234 * been called before it is garbage-collected. The intended safety 1235 * net is weak because the JVM does not guarantee that an object 1236 * will be garbage-collected when it is no longer referenced. Users 1237 * of the API should call {@code close()} to ensure that all 1238 * resources associated with this consumer are reclaimed in a timely 1239 * manner. 1240 * 1241 * @see #close() 1242 */ 1243 protected void 1244 finalize() 1245 { 1246 close(); 1247 } 1248 1249 private String 1250 getTag() 1251 { 1252 return super.toString(); 1253 } 1254 1255 // 1256 // Uniquely identifies a consumer across systems so it is possible 1257 // to validate that an object such as a Program passed to a remote 1258 // client over a socket was created by this consumer and no other. 1259 // 1260 static class Identifier implements Serializable { 1261 static final long serialVersionUID = 2183165132305302834L; 1262 1263 // local identifier 1264 private int id; 1265 private long timestamp; 1266 // remote identifier 1267 private InetAddress localHost; 1268 private String tag; // in case localHost not available 1269 1270 private 1271 Identifier(LocalConsumer consumer) 1272 { 1273 id = LocalConsumer.sequence++; 1274 timestamp = System.currentTimeMillis(); 1275 try { 1276 localHost = InetAddress.getLocalHost(); 1277 } catch (UnknownHostException e) { 1278 localHost = null; 1279 } 1280 tag = consumer.getTag(); 1281 } 1282 1283 @Override 1284 public boolean 1285 equals(Object o) 1286 { 1287 if (o == this) { 1288 return true; 1289 } 1290 if (o instanceof Identifier) { 1291 Identifier i = (Identifier)o; 1292 return ((id == i.id) && 1293 (timestamp == i.timestamp) && 1294 ((localHost == null) ? (i.localHost == null) : 1295 localHost.equals(i.localHost)) && 1296 tag.equals(i.tag)); 1297 } 1298 return false; 1299 } 1300 1301 @Override 1302 public int 1303 hashCode() 1304 { 1305 int hash = 17; 1306 hash = (37 * hash) + id; 1307 hash = (37 * hash) + ((int)(timestamp ^ (timestamp >>> 32))); 1308 hash = (37 * hash) + (localHost == null ? 0 : 1309 localHost.hashCode()); 1310 hash = (37 * hash) + tag.hashCode(); 1311 return hash; 1312 } 1313 1314 @Override 1315 public String 1316 toString() 1317 { 1318 StringBuffer buf = new StringBuffer(); 1319 buf.append(Identifier.class.getName()); 1320 buf.append("[id = "); 1321 buf.append(id); 1322 buf.append(", timestamp = "); 1323 buf.append(timestamp); 1324 buf.append(", localHost = "); 1325 buf.append(localHost); 1326 buf.append(", tag = "); 1327 buf.append(tag); 1328 buf.append(']'); 1329 return buf.toString(); 1330 } 1331 } 1332 } 1333