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 // 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 state = State.COMPILED; 415 416 return p; 417 } 418 419 private synchronized void 420 checkProgram(Program program) 421 { 422 if (program == null) { 423 throw new NullPointerException("program is null"); 424 } 425 if (!id.equals(program.consumerID)) { 426 throw new IllegalArgumentException("program not compiled " + 427 "by this consumer"); 428 } 429 } 430 431 public void 432 enable() throws DTraceException 433 { 434 enable(null); 435 } 436 437 public synchronized void 438 enable(Program program) throws DTraceException 439 { 440 switch (state) { 441 case INIT: 442 throw new IllegalStateException("consumer not open"); 443 case OPEN: 444 throw new IllegalStateException("no compiled program"); 445 case COMPILED: 446 break; 447 case GO: 448 case STARTED: 449 throw new IllegalStateException("go() already called"); 450 case STOPPED: 451 throw new IllegalStateException("consumer stopped"); 452 case CLOSED: 453 throw new IllegalStateException("consumer closed"); 454 } 455 456 // Compile all programs if null 457 if (program != null) { 458 checkProgram(program); 459 } 460 461 // 462 // Left to native code to throw IllegalArgumentException if the 463 // program is already enabled, since only the native code knows 464 // the enabled state. 465 // 466 synchronized (LocalConsumer.class) { 467 _exec(program); 468 } 469 } 470 471 public synchronized void 472 getProgramInfo(Program program) throws DTraceException 473 { 474 checkProgram(program); 475 if (state == State.CLOSED) { 476 throw new IllegalStateException("consumer closed"); 477 } 478 479 // 480 // The given program was compiled by this consumer, so we can 481 // assert the following: 482 // 483 assert ((state != State.INIT) && (state != State.OPEN)); 484 485 synchronized (LocalConsumer.class) { 486 _getProgramInfo(program); 487 } 488 } 489 490 private void 491 setOptions(Option[] options) throws DTraceException 492 { 493 for (Option o : options) { 494 setOption(o.getName(), o.getValue()); 495 } 496 } 497 498 public void 499 setOption(String option) throws DTraceException 500 { 501 setOption(option, Option.VALUE_SET); 502 } 503 504 public void 505 unsetOption(String option) throws DTraceException 506 { 507 setOption(option, Option.VALUE_UNSET); 508 } 509 510 public synchronized void 511 setOption(String option, String value) throws DTraceException 512 { 513 if (option == null) { 514 throw new NullPointerException("option is null"); 515 } 516 if (value == null) { 517 throw new NullPointerException("option value is null"); 518 } 519 520 switch (state) { 521 case INIT: 522 throw new IllegalStateException("consumer not open"); 523 case OPEN: 524 case COMPILED: 525 case GO: 526 case STARTED: // Some options can be set on a running consumer 527 case STOPPED: // Allowed (may affect getAggregate()) 528 break; 529 case CLOSED: 530 throw new IllegalStateException("consumer closed"); 531 } 532 533 synchronized (LocalConsumer.class) { 534 _setOption(option, value); 535 } 536 } 537 538 public synchronized long 539 getOption(String option) throws DTraceException 540 { 541 if (option == null) { 542 throw new NullPointerException("option is null"); 543 } 544 545 switch (state) { 546 case INIT: 547 throw new IllegalStateException("consumer not open"); 548 case OPEN: 549 case COMPILED: 550 case GO: 551 case STARTED: 552 case STOPPED: 553 break; 554 case CLOSED: 555 throw new IllegalStateException("consumer closed"); 556 } 557 558 long value; 559 synchronized (LocalConsumer.class) { 560 value = _getOption(option); 561 } 562 return value; 563 } 564 565 public final synchronized boolean 566 isOpen() 567 { 568 return ((state != State.INIT) && (state != State.CLOSED)); 569 } 570 571 public final synchronized boolean 572 isEnabled() 573 { 574 if (state != State.COMPILED) { 575 return false; 576 } 577 578 return _isEnabled(); 579 } 580 581 public final synchronized boolean 582 isRunning() 583 { 584 return (state == State.STARTED); 585 } 586 587 public final synchronized boolean 588 isClosed() 589 { 590 return (state == State.CLOSED); 591 } 592 593 /** 594 * Called in the runnable target of the thread returned by {@link 595 * #createThread()} to run this DTrace consumer. 596 * 597 * @see #createThread() 598 */ 599 protected final void 600 work() 601 { 602 try { 603 synchronized (this) { 604 if (state != State.GO) { 605 // 606 // stop() was called after go() but before the 607 // consumer started 608 // 609 return; // executes finally block before returning 610 } 611 612 state = State.STARTED; 613 fireConsumerStarted(new ConsumerEvent(this, 614 System.nanoTime())); 615 } 616 617 // 618 // We should not prevent other consumers from running 619 // concurrently while this consumer blocks on the native 620 // consumer loop. Instead, native code will acquire the 621 // LocalConsumer.class monitor as needed before calling 622 // libdtrace functions. 623 // 624 _consume(); 625 626 } catch (Throwable e) { 627 if (exceptionHandler != null) { 628 exceptionHandler.handleException(e); 629 } else { 630 e.printStackTrace(); 631 } 632 } finally { 633 synchronized (stopLock) { 634 // Notify listeners while holding stopLock to guarantee 635 // that listeners finish executing consumerStopped() 636 // before the stop() method returns. 637 synchronized (this) { 638 state = State.STOPPED; 639 fireConsumerStopped(new ConsumerEvent(this, 640 System.nanoTime())); 641 } 642 643 // Notify the stop() method to stop waiting 644 workEnded = true; 645 stopLock.notifyAll(); 646 } 647 } 648 } 649 650 /** 651 * Creates the background thread started by {@link #go()} to run 652 * this consumer. Override this method if you need to set 653 * non-default {@code Thread} options or create the thread in a 654 * {@code ThreadGroup}. If you don't need to create the thread 655 * yourself, set the desired options on {@code super.createThread()} 656 * before returning it. Otherwise, the {@code Runnable} target of 657 * the created thread must call {@link #work()} in order to run this 658 * DTrace consumer. For example, to modify the default background 659 * consumer thread: 660 * <pre><code> 661 * protected Thread 662 * createThread() 663 * { 664 * Thread t = super.createThread(); 665 * t.setPriority(Thread.MIN_PRIORITY); 666 * return t; 667 * } 668 * </code></pre> 669 * Or if you need to create your own thread: 670 * <pre></code> 671 * protected Thread 672 * createThread() 673 * { 674 * Runnable target = new Runnable() { 675 * public void run() { 676 * work(); 677 * } 678 * }; 679 * String name = "Consumer " + UserApplication.sequence++; 680 * Thread t = new Thread(UserApplication.threadGroup, 681 * target, name); 682 * return t; 683 * } 684 * </code></pre> 685 * Do not start the returned thread, otherwise {@code go()} will 686 * throw an {@link IllegalThreadStateException} when it tries to 687 * start the returned thread a second time. 688 */ 689 protected Thread 690 createThread() 691 { 692 Thread t = new Thread(new Runnable() { 693 public void run() { 694 work(); 695 } 696 }, "DTrace consumer " + id); 697 return t; 698 } 699 700 /** 701 * @inheritDoc 702 * @throws IllegalThreadStateException if a subclass calls {@link 703 * Thread#start()} on the value of {@link #createThread()} 704 * @see #createThread() 705 */ 706 public void 707 go() throws DTraceException 708 { 709 go(null); 710 } 711 712 /** 713 * @inheritDoc 714 * @throws IllegalThreadStateException if a subclass calls {@link 715 * Thread#start()} on the value of {@link #createThread()} 716 * @see #createThread() 717 */ 718 public synchronized void 719 go(ExceptionHandler h) throws DTraceException 720 { 721 switch (state) { 722 case INIT: 723 throw new IllegalStateException("consumer not open"); 724 case OPEN: 725 throw new IllegalStateException("no compiled program"); 726 case COMPILED: 727 // 728 // Throws IllegalStateException if not all compiled programs are 729 // also enabled. Does not make any calls to libdtrace. 730 // 731 _checkProgramEnabling(); 732 break; 733 case GO: 734 case STARTED: 735 throw new IllegalStateException("go() already called"); 736 case STOPPED: 737 throw new IllegalStateException("consumer stopped"); 738 case CLOSED: 739 throw new IllegalStateException("consumer closed"); 740 default: 741 throw new IllegalArgumentException("unknown state: " + state); 742 } 743 744 synchronized (LocalConsumer.class) { 745 _go(); 746 } 747 748 state = State.GO; 749 exceptionHandler = h; 750 Thread t = createThread(); 751 t.start(); 752 } 753 754 /** 755 * @inheritDoc 756 * 757 * @throws IllegalThreadStateException if attempting to {@code 758 * stop()} a running consumer while holding the lock on that 759 * consumer 760 */ 761 public void 762 stop() 763 { 764 boolean running = false; 765 766 synchronized (this) { 767 switch (state) { 768 case INIT: 769 throw new IllegalStateException("consumer not open"); 770 case OPEN: 771 case COMPILED: 772 throw new IllegalStateException("go() not called"); 773 case GO: 774 try { 775 synchronized (LocalConsumer.class) { 776 _stop(); 777 } 778 state = State.STOPPED; 779 } catch (DTraceException e) { 780 if (exceptionHandler != null) { 781 exceptionHandler.handleException(e); 782 } else { 783 e.printStackTrace(); 784 } 785 } 786 break; 787 case STARTED: 788 running = true; 789 break; 790 case STOPPED: 791 // 792 // The work() thread that runs the native consumer 793 // loop may have terminated because of the exit() 794 // action in a DTrace program. In that case, a 795 // RuntimeException is inappropriate because there 796 // is no misuse of the API. Creating a new checked 797 // exception type to handle this case seems to offer 798 // no benefit for the trouble to the caller. 799 // Instead, the situation calls for stop() to be 800 // quietly tolerant. 801 // 802 if (stopCalled) { 803 throw new IllegalStateException( 804 "consumer already stopped"); 805 } 806 logger.fine("consumer already stopped"); 807 break; 808 case CLOSED: 809 throw new IllegalStateException("consumer closed"); 810 default: 811 throw new IllegalArgumentException("unknown state: " + 812 state); 813 } 814 815 stopCalled = true; 816 } 817 818 if (running) { 819 if (Thread.holdsLock(this)) { 820 throw new IllegalThreadStateException("The current " + 821 "thread cannot stop this LocalConsumer while " + 822 "holding the lock on this LocalConsumer"); 823 } 824 825 // 826 // Calls no libdtrace methods, so no synchronization is 827 // needed. Sets a native flag that causes the consumer 828 // thread to exit the consumer loop and call native 829 // dtrace_stop() at the end of the current interval (after 830 // grabbing the global Consumer.class lock required for any 831 // libdtrace call). 832 // 833 _interrupt(); 834 835 synchronized (stopLock) { 836 // 837 // Wait for work() to set workEnded. If the work() 838 // thread got the stopLock first, then workEnded is 839 // already set. 840 // 841 while (!workEnded) { 842 try { 843 stopLock.wait(); 844 } catch (InterruptedException e) { 845 logger.warning(e.toString()); 846 // do nothing but re-check the condition for 847 // waiting 848 } 849 } 850 } 851 } 852 } 853 854 public synchronized void 855 abort() 856 { 857 if ((state != State.INIT) && (state != State.CLOSED)) { 858 _interrupt(); 859 } 860 abortCalled = true; 861 } 862 863 /** 864 * @inheritDoc 865 * 866 * @throws IllegalThreadStateException if attempting to {@code 867 * close()} a running consumer while holding the lock on that 868 * consumer 869 */ 870 public void 871 close() 872 { 873 synchronized (this) { 874 if ((state == State.INIT) || (state == State.CLOSED)) { 875 state = State.CLOSED; 876 return; 877 } 878 } 879 880 try { 881 stop(); 882 } catch (IllegalStateException e) { 883 // ignore (we don't have synchronized state access because 884 // it is illegal to call stop() while holding the lock on 885 // this consumer) 886 } 887 888 synchronized (this) { 889 if (state != State.CLOSED) { 890 synchronized (LocalConsumer.class) { 891 _close(); 892 } 893 _destroy(); 894 state = State.CLOSED; 895 896 if (logger.isLoggable(Level.INFO)) { 897 logger.info("consumer table count: " + _openCount()); 898 } 899 } 900 } 901 } 902 903 public void 904 addConsumerListener(ConsumerListener l) 905 { 906 listenerList.add(ConsumerListener.class, l); 907 } 908 909 public void 910 removeConsumerListener(ConsumerListener l) 911 { 912 listenerList.remove(ConsumerListener.class, l); 913 } 914 915 public Aggregate 916 getAggregate() throws DTraceException 917 { 918 // include all, clear none 919 return getAggregate(null, Collections. <String> emptySet()); 920 } 921 922 public Aggregate 923 getAggregate(Set <String> includedAggregationNames) 924 throws DTraceException 925 { 926 return getAggregate(includedAggregationNames, 927 Collections. <String> emptySet()); 928 } 929 930 public Aggregate 931 getAggregate(Set <String> includedAggregationNames, 932 Set <String> clearedAggregationNames) 933 throws DTraceException 934 { 935 AggregateSpec spec = new AggregateSpec(); 936 937 if (includedAggregationNames == null) { 938 spec.setIncludeByDefault(true); 939 } else { 940 spec.setIncludeByDefault(false); 941 for (String included : includedAggregationNames) { 942 spec.addIncludedAggregationName(included); 943 } 944 } 945 946 if (clearedAggregationNames == null) { 947 spec.setClearByDefault(true); 948 } else { 949 spec.setClearByDefault(false); 950 for (String cleared : clearedAggregationNames) { 951 spec.addClearedAggregationName(cleared); 952 } 953 } 954 955 return getAggregate(spec); 956 } 957 958 private synchronized Aggregate 959 getAggregate(AggregateSpec spec) throws DTraceException 960 { 961 // 962 // It should be possible to request aggregation data after a 963 // consumer has stopped but not after it has been closed. 964 // 965 checkGoCalled(); 966 967 // 968 // Getting the aggregate is a time-consuming request that should not 969 // prevent other consumers from running concurrently. Instead, 970 // native code will acquire the LocalConsumer.class monitor as 971 // needed before calling libdtrace functions. 972 // 973 Aggregate aggregate = _getAggregate(spec); 974 return aggregate; 975 } 976 977 private synchronized void 978 checkGoCalled() 979 { 980 switch (state) { 981 case INIT: 982 throw new IllegalStateException("consumer not open"); 983 case OPEN: 984 case COMPILED: 985 throw new IllegalStateException("go() not called"); 986 case GO: 987 case STARTED: 988 case STOPPED: 989 break; 990 case CLOSED: 991 throw new IllegalStateException("consumer closed"); 992 } 993 } 994 995 private synchronized void 996 checkGoNotCalled() 997 { 998 switch (state) { 999 case INIT: 1000 throw new IllegalStateException("consumer not open"); 1001 case OPEN: 1002 case COMPILED: 1003 break; 1004 case GO: 1005 case STARTED: 1006 throw new IllegalStateException("go() already called"); 1007 case STOPPED: 1008 throw new IllegalStateException("consumer stopped"); 1009 case CLOSED: 1010 throw new IllegalStateException("consumer closed"); 1011 } 1012 } 1013 1014 public synchronized int 1015 createProcess(String command) throws DTraceException 1016 { 1017 if (command == null) { 1018 throw new NullPointerException("command is null"); 1019 } 1020 1021 checkGoNotCalled(); 1022 1023 int pid; 1024 synchronized (LocalConsumer.class) { 1025 pid = _createProcess(command); 1026 } 1027 return pid; 1028 } 1029 1030 public synchronized void 1031 grabProcess(int pid) throws DTraceException 1032 { 1033 checkGoNotCalled(); 1034 1035 synchronized (LocalConsumer.class) { 1036 _grabProcess(pid); 1037 } 1038 } 1039 1040 public synchronized List <ProbeDescription> 1041 listProbes(ProbeDescription filter) throws DTraceException 1042 { 1043 checkGoNotCalled(); 1044 List <ProbeDescription> probeList = 1045 new LinkedList <ProbeDescription> (); 1046 if (filter == ProbeDescription.EMPTY) { 1047 filter = null; 1048 } 1049 synchronized (LocalConsumer.class) { 1050 _listProbes(probeList, filter); 1051 } 1052 return probeList; 1053 } 1054 1055 public synchronized List <Probe> 1056 listProbeDetail(ProbeDescription filter) throws DTraceException 1057 { 1058 checkGoNotCalled(); 1059 List <Probe> probeList = new LinkedList <Probe> (); 1060 if (filter == ProbeDescription.EMPTY) { 1061 filter = null; 1062 } 1063 synchronized (LocalConsumer.class) { 1064 _listProbeDetail(probeList, filter); 1065 } 1066 return probeList; 1067 } 1068 1069 public synchronized List <ProbeDescription> 1070 listProgramProbes(Program program) throws DTraceException 1071 { 1072 checkProgram(program); 1073 checkGoNotCalled(); 1074 List <ProbeDescription> probeList = 1075 new LinkedList <ProbeDescription> (); 1076 synchronized (LocalConsumer.class) { 1077 _listCompiledProbes(probeList, program); 1078 } 1079 return probeList; 1080 } 1081 1082 public synchronized List <Probe> 1083 listProgramProbeDetail(Program program) throws DTraceException 1084 { 1085 checkProgram(program); 1086 checkGoNotCalled(); 1087 List <Probe> probeList = new LinkedList <Probe> (); 1088 synchronized (LocalConsumer.class) { 1089 _listCompiledProbeDetail(probeList, program); 1090 } 1091 return probeList; 1092 } 1093 1094 public synchronized String 1095 lookupKernelFunction(int address) 1096 { 1097 checkGoCalled(); 1098 synchronized (LocalConsumer.class) { 1099 return _lookupKernelFunction(new Integer(address)); 1100 } 1101 } 1102 1103 public synchronized String 1104 lookupKernelFunction(long address) 1105 { 1106 checkGoCalled(); 1107 synchronized (LocalConsumer.class) { 1108 return _lookupKernelFunction(new Long(address)); 1109 } 1110 } 1111 1112 public synchronized String 1113 lookupUserFunction(int pid, int address) 1114 { 1115 checkGoCalled(); 1116 synchronized (LocalConsumer.class) { 1117 return _lookupUserFunction(pid, new Integer(address)); 1118 } 1119 } 1120 1121 public synchronized String 1122 lookupUserFunction(int pid, long address) 1123 { 1124 checkGoCalled(); 1125 synchronized (LocalConsumer.class) { 1126 return _lookupUserFunction(pid, new Long(address)); 1127 } 1128 } 1129 1130 public String 1131 getVersion() 1132 { 1133 synchronized (LocalConsumer.class) { 1134 return LocalConsumer._getVersion(); 1135 } 1136 } 1137 1138 /** 1139 * Called by native code. 1140 */ 1141 private void 1142 nextProbeData(ProbeData probeData) throws ConsumerException 1143 { 1144 fireDataReceived(new DataEvent(this, probeData)); 1145 } 1146 1147 /** 1148 * Called by native code. 1149 */ 1150 private void 1151 dataDropped(Drop drop) throws ConsumerException 1152 { 1153 fireDataDropped(new DropEvent(this, drop)); 1154 } 1155 1156 /** 1157 * Called by native code. 1158 */ 1159 private void 1160 errorEncountered(Error error) throws ConsumerException 1161 { 1162 fireErrorEncountered(new ErrorEvent(this, error)); 1163 } 1164 1165 /** 1166 * Called by native code. 1167 */ 1168 private void 1169 processStateChanged(ProcessState processState) throws ConsumerException 1170 { 1171 fireProcessStateChanged(new ProcessEvent(this, processState)); 1172 } 1173 1174 protected void 1175 fireDataReceived(DataEvent e) throws ConsumerException 1176 { 1177 // Guaranteed to return a non-null array 1178 Object[] listeners = listenerList.getListenerList(); 1179 // Process the listeners last to first, notifying 1180 // those that are interested in this event 1181 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1182 if (listeners[i] == ConsumerListener.class) { 1183 ((ConsumerListener)listeners[i + 1]).dataReceived(e); 1184 } 1185 } 1186 } 1187 1188 protected void 1189 fireDataDropped(DropEvent e) throws ConsumerException 1190 { 1191 // Guaranteed to return a non-null array 1192 Object[] listeners = listenerList.getListenerList(); 1193 // Process the listeners last to first, notifying 1194 // those that are interested in this event 1195 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1196 if (listeners[i] == ConsumerListener.class) { 1197 ((ConsumerListener)listeners[i + 1]).dataDropped(e); 1198 } 1199 } 1200 } 1201 1202 protected void 1203 fireErrorEncountered(ErrorEvent e) throws ConsumerException 1204 { 1205 // Guaranteed to return a non-null array 1206 Object[] listeners = listenerList.getListenerList(); 1207 // Process the listeners last to first, notifying 1208 // those that are interested in this event 1209 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1210 if (listeners[i] == ConsumerListener.class) { 1211 ((ConsumerListener)listeners[i + 1]).errorEncountered(e); 1212 } 1213 } 1214 } 1215 1216 protected void 1217 fireProcessStateChanged(ProcessEvent e) throws ConsumerException 1218 { 1219 // Guaranteed to return a non-null array 1220 Object[] listeners = listenerList.getListenerList(); 1221 // Process the listeners last to first, notifying 1222 // those that are interested in this event 1223 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1224 if (listeners[i] == ConsumerListener.class) { 1225 ((ConsumerListener)listeners[i + 1]).processStateChanged(e); 1226 } 1227 } 1228 } 1229 1230 protected void 1231 fireConsumerStarted(ConsumerEvent e) 1232 { 1233 // Guaranteed to return a non-null array 1234 Object[] listeners = listenerList.getListenerList(); 1235 // Process the listeners last to first, notifying 1236 // those that are interested in this event 1237 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1238 if (listeners[i] == ConsumerListener.class) { 1239 ((ConsumerListener)listeners[i + 1]).consumerStarted(e); 1240 } 1241 } 1242 } 1243 1244 protected void 1245 fireConsumerStopped(ConsumerEvent e) 1246 { 1247 // Guaranteed to return a non-null array 1248 Object[] listeners = listenerList.getListenerList(); 1249 // Process the listeners last to first, notifying 1250 // those that are interested in this event 1251 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1252 if (listeners[i] == ConsumerListener.class) { 1253 ((ConsumerListener)listeners[i + 1]).consumerStopped(e); 1254 } 1255 } 1256 } 1257 1258 // Called by native code 1259 private void 1260 intervalBegan() 1261 { 1262 fireIntervalBegan(new ConsumerEvent(this, System.nanoTime())); 1263 } 1264 1265 protected void 1266 fireIntervalBegan(ConsumerEvent e) 1267 { 1268 // Guaranteed to return a non-null array 1269 Object[] listeners = listenerList.getListenerList(); 1270 // Process the listeners last to first, notifying 1271 // those that are interested in this event 1272 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1273 if (listeners[i] == ConsumerListener.class) { 1274 ((ConsumerListener)listeners[i + 1]).intervalBegan(e); 1275 } 1276 } 1277 } 1278 1279 // Called by native code 1280 private void 1281 intervalEnded() 1282 { 1283 fireIntervalEnded(new ConsumerEvent(this, System.nanoTime())); 1284 } 1285 1286 protected void 1287 fireIntervalEnded(ConsumerEvent e) 1288 { 1289 // Guaranteed to return a non-null array 1290 Object[] listeners = listenerList.getListenerList(); 1291 // Process the listeners last to first, notifying 1292 // those that are interested in this event 1293 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1294 if (listeners[i] == ConsumerListener.class) { 1295 ((ConsumerListener)listeners[i + 1]).intervalEnded(e); 1296 } 1297 } 1298 } 1299 1300 /** 1301 * Gets a string representation of this consumer useful for logging 1302 * and not intended for display. The exact details of the 1303 * representation are unspecified and subject to change, but the 1304 * following format may be regarded as typical: 1305 * <pre><code> 1306 * class-name[property1 = value1, property2 = value2] 1307 * </code></pre> 1308 */ 1309 public String 1310 toString() 1311 { 1312 StringBuffer buf = new StringBuffer(LocalConsumer.class.getName()); 1313 synchronized (this) { 1314 buf.append("[open = "); 1315 buf.append(isOpen()); 1316 buf.append(", enabled = "); 1317 buf.append(isEnabled()); 1318 buf.append(", running = "); 1319 buf.append(isRunning()); 1320 buf.append(", closed = "); 1321 buf.append(isClosed()); 1322 } 1323 buf.append(']'); 1324 return buf.toString(); 1325 } 1326 1327 /** 1328 * Ensures that the {@link #close()} method of this consumer has 1329 * been called before it is garbage-collected. The intended safety 1330 * net is weak because the JVM does not guarantee that an object 1331 * will be garbage-collected when it is no longer referenced. Users 1332 * of the API should call {@code close()} to ensure that all 1333 * resources associated with this consumer are reclaimed in a timely 1334 * manner. 1335 * 1336 * @see #close() 1337 */ 1338 protected void 1339 finalize() 1340 { 1341 close(); 1342 } 1343 1344 private String 1345 getTag() 1346 { 1347 return super.toString(); 1348 } 1349 1350 // 1351 // Uniquely identifies a consumer across systems so it is possible 1352 // to validate that an object such as a Program passed to a remote 1353 // client over a socket was created by this consumer and no other. 1354 // 1355 static class Identifier implements Serializable { 1356 static final long serialVersionUID = 2183165132305302834L; 1357 1358 // local identifier 1359 private int id; 1360 private long timestamp; 1361 // remote identifier 1362 private InetAddress localHost; 1363 private String tag; // in case localHost not available 1364 1365 private 1366 Identifier(LocalConsumer consumer) 1367 { 1368 id = LocalConsumer.sequence++; 1369 timestamp = System.currentTimeMillis(); 1370 try { 1371 localHost = InetAddress.getLocalHost(); 1372 } catch (UnknownHostException e) { 1373 localHost = null; 1374 } 1375 tag = consumer.getTag(); 1376 } 1377 1378 @Override 1379 public boolean 1380 equals(Object o) 1381 { 1382 if (o == this) { 1383 return true; 1384 } 1385 if (o instanceof Identifier) { 1386 Identifier i = (Identifier)o; 1387 return ((id == i.id) && 1388 (timestamp == i.timestamp) && 1389 ((localHost == null) ? (i.localHost == null) : 1390 localHost.equals(i.localHost)) && 1391 tag.equals(i.tag)); 1392 } 1393 return false; 1394 } 1395 1396 @Override 1397 public int 1398 hashCode() 1399 { 1400 int hash = 17; 1401 hash = (37 * hash) + id; 1402 hash = (37 * hash) + ((int)(timestamp ^ (timestamp >>> 32))); 1403 hash = (37 * hash) + (localHost == null ? 0 : 1404 localHost.hashCode()); 1405 hash = (37 * hash) + tag.hashCode(); 1406 return hash; 1407 } 1408 1409 @Override 1410 public String 1411 toString() 1412 { 1413 StringBuffer buf = new StringBuffer(); 1414 buf.append(Identifier.class.getName()); 1415 buf.append("[id = "); 1416 buf.append(id); 1417 buf.append(", timestamp = "); 1418 buf.append(timestamp); 1419 buf.append(", localHost = "); 1420 buf.append(localHost); 1421 buf.append(", tag = "); 1422 buf.append(tag); 1423 buf.append(']'); 1424 return buf.toString(); 1425 } 1426 } 1427 } 1428