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