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