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