1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 package org.opensolaris.os.dtrace; 27 28 import java.io.*; 29 import java.util.*; 30 import java.net.InetAddress; 31 import java.net.UnknownHostException; 32 import javax.swing.event.EventListenerList; 33 import java.util.logging.*; 34 35 /** 36 * Interface to the native DTrace library, each instance is a single 37 * DTrace consumer. 38 * 39 * @author Tom Erickson 40 */ 41 public class LocalConsumer implements Consumer { 42 // 43 // Implementation notes: 44 // 45 // libdtrace is *not* thread-safe. You cannot make multiple calls 46 // into it simultaneously from different threads, even if those 47 // threads are operating on different dtrace_hdl_t's. Calls to 48 // libdtrace are synchronized on a global lock, LocalConsumer.class. 49 50 static Logger logger = Logger.getLogger(LocalConsumer.class.getName()); 51 52 // Needs to match the version in dtrace_jni.c 53 private static final int DTRACE_JNI_VERSION = 3; 54 55 private static final Option[] DEFAULT_OPTIONS = new Option[] { 56 new Option(Option.bufsize, Option.kb(256)), 57 new Option(Option.aggsize, Option.kb(256)), 58 }; 59 _loadJniTable()60 private static native void _loadJniTable(); 61 62 // Undocumented configuration options 63 private static boolean debug; 64 private static int maxConsumers; 65 66 static { LocalConsumer.configureLogging()67 LocalConsumer.configureLogging(); 68 // Undocumented configuration options settable using 69 // java -Doption=value LocalConsumer.getConfigurationOptions()70 LocalConsumer.getConfigurationOptions(); 71 72 Utility.loadLibrary("libdtrace_jni.so.1", debug); 73 74 _checkVersion(DTRACE_JNI_VERSION); 75 _setDebug(debug); 76 if (maxConsumers > 0) { 77 _setMaximumConsumers(maxConsumers); 78 } 79 80 // 81 // Last of all in case configuration options affect the loading 82 // of the JNI table. 83 // _loadJniTable()84 _loadJniTable(); 85 } 86 87 // Native JNI interface (see lib/libdtrace_jni/dtrace_jni.c) _checkVersion(int version)88 private static native void _checkVersion(int version); _open(OpenFlag[] flags)89 private native void _open(OpenFlag[] flags) throws DTraceException; _compileString(String program, String[] args)90 private native Program _compileString(String program, String[] args) 91 throws DTraceException; _compileFile(String path, String[] args)92 private native Program.File _compileFile(String path, String[] args) 93 throws DTraceException; _exec(Program program)94 private native void _exec(Program program) throws DTraceException; _getProgramInfo(Program program)95 private native void _getProgramInfo(Program program) 96 throws DTraceException; _setOption(String option, String value)97 private native void _setOption(String option, String value) 98 throws DTraceException; _getOption(String option)99 private native long _getOption(String option) throws DTraceException; _isEnabled()100 private native boolean _isEnabled(); _checkProgramEnabling()101 private native void _checkProgramEnabling(); _go()102 private native void _go() throws DTraceException; _stop()103 private native void _stop() throws DTraceException; _consume()104 private native void _consume() throws DTraceException; _interrupt()105 private native void _interrupt(); _close()106 private native void _close(); _getAggregate(AggregateSpec spec)107 private native Aggregate _getAggregate(AggregateSpec spec) 108 throws DTraceException; _createProcess(String cmd)109 private native int _createProcess(String cmd) throws DTraceException; _grabProcess(int pid)110 private native void _grabProcess(int pid) throws DTraceException; _listProbes(List <ProbeDescription> probeList, ProbeDescription filter)111 private native void _listProbes(List <ProbeDescription> probeList, 112 ProbeDescription filter); _listProbeDetail(List <Probe> probeList, ProbeDescription filter)113 private native void _listProbeDetail(List <Probe> probeList, 114 ProbeDescription filter); _listCompiledProbes( List <ProbeDescription> probeList, Program program)115 private native void _listCompiledProbes( 116 List <ProbeDescription> probeList, Program program); _listCompiledProbeDetail( List <Probe> probeList, Program program)117 private native void _listCompiledProbeDetail( 118 List <Probe> probeList, Program program); _getVersion()119 private static native String _getVersion(); _openCount()120 private static native int _openCount(); 121 // 122 // Releases memory held in the JNI layer after dtrace_close() has 123 // released critical system resources like file descriptors, and 124 // calls to libdtrace are no longer needed (or possible). 125 // _destroy()126 private native void _destroy(); 127 // Called by LogDistribution _quantizeBucket(int i)128 static native long _quantizeBucket(int i); 129 // 130 // Cannot be static because the necessary dtrace handle is specific 131 // to this Consumer. 132 // _lookupKernelFunction(Number address)133 private native String _lookupKernelFunction(Number address); _lookupUserFunction(int pid, Number address)134 private native String _lookupUserFunction(int pid, Number address); _getExecutableName()135 private static native String _getExecutableName(); 136 137 // Undocumented configuration options _setMaximumConsumers(int max)138 private static native void _setMaximumConsumers(int max); _setDebug(boolean debug)139 private static native void _setDebug(boolean debug); 140 141 protected EventListenerList listenerList; 142 protected ExceptionHandler exceptionHandler; 143 144 private int _handle = -1; // native C identifier (do not modify) 145 private final Identifier id; // java identifier 146 147 private enum State { 148 INIT, 149 OPEN, 150 COMPILED, 151 GO, 152 STARTED, 153 STOPPED, 154 CLOSED 155 } 156 157 private State state = State.INIT; 158 private boolean stopCalled; 159 private boolean abortCalled; 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 configureLogging()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 getIntegerProperty(String name)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 getConfigurationOptions()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 LocalConsumer()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 getHandle()273 getHandle() 274 { 275 return _handle; 276 } 277 278 /** 279 * Called by native C code only 280 */ 281 private void setHandle(int n)282 setHandle(int n) 283 { 284 _handle = n; 285 } 286 287 public synchronized void open(OpenFlag .... flags)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 (abortCalled) { 311 _interrupt(); 312 } 313 314 if (logger.isLoggable(Level.INFO)) { 315 logger.info("consumer table count: " + _openCount()); 316 } 317 } 318 319 private synchronized void checkCompile()320 checkCompile() 321 { 322 switch (state) { 323 case INIT: 324 throw new IllegalStateException("consumer not open"); 325 case OPEN: 326 case COMPILED: // caller may compile more than one program 327 break; 328 case GO: 329 case STARTED: 330 throw new IllegalStateException("go() already called"); 331 case STOPPED: 332 throw new IllegalStateException("consumer stopped"); 333 case CLOSED: 334 throw new IllegalStateException("consumer closed"); 335 } 336 } 337 338 public synchronized Program compile(String program, String ... macroArgs)339 compile(String program, String ... macroArgs) throws DTraceException 340 { 341 if (program == null) { 342 throw new NullPointerException("program string is null"); 343 } 344 checkCompile(); 345 Program p = null; 346 347 String[] argv = null; 348 if (macroArgs != null) { 349 for (String macroArg : macroArgs) { 350 if (macroArg == null) { 351 throw new NullPointerException("macro argument is null"); 352 } 353 } 354 argv = new String[macroArgs.length + 1]; 355 synchronized (LocalConsumer.class) { 356 // 357 // Could be an application with an embedded JVM, not 358 // necessarily "java". 359 // 360 argv[0] = _getExecutableName(); 361 } 362 System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); 363 } else { 364 synchronized (LocalConsumer.class) { 365 argv = new String[] { _getExecutableName() }; 366 } 367 } 368 synchronized (LocalConsumer.class) { 369 p = _compileString(program, argv); 370 } 371 p.consumerID = id; 372 p.contents = program; 373 p.validate(); 374 state = State.COMPILED; 375 376 return p; 377 } 378 379 public synchronized Program compile(File program, String ... macroArgs)380 compile(File program, String ... macroArgs) throws DTraceException, 381 IOException, SecurityException 382 { 383 if (program == null) { 384 throw new NullPointerException("program file is null"); 385 } 386 if (!program.canRead()) { 387 throw new FileNotFoundException("failed to open " + 388 program.getName()); 389 } 390 checkCompile(); 391 Program.File p = null; 392 393 String[] argv = null; 394 if (macroArgs != null) { 395 for (String macroArg : macroArgs) { 396 if (macroArg == null) { 397 throw new NullPointerException("macro argument is null"); 398 } 399 } 400 argv = new String[macroArgs.length + 1]; 401 argv[0] = program.getPath(); 402 System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length); 403 } else { 404 macroArgs = new String[] { program.getPath() }; 405 } 406 synchronized (LocalConsumer.class) { 407 p = _compileFile(program.getPath(), argv); 408 } 409 p.consumerID = id; 410 p.contents = Program.getProgramString(program); 411 p.file = program; 412 p.validate(); 413 p.validateFile(); 414 state = State.COMPILED; 415 416 return p; 417 } 418 419 private synchronized void checkProgram(Program program)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 enable()432 enable() throws DTraceException 433 { 434 enable(null); 435 } 436 437 public synchronized void enable(Program program)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 getProgramInfo(Program program)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 setOptions(Option[] options)491 setOptions(Option[] options) throws DTraceException 492 { 493 for (Option o : options) { 494 setOption(o.getName(), o.getValue()); 495 } 496 } 497 498 public void setOption(String option)499 setOption(String option) throws DTraceException 500 { 501 setOption(option, Option.VALUE_SET); 502 } 503 504 public void unsetOption(String option)505 unsetOption(String option) throws DTraceException 506 { 507 setOption(option, Option.VALUE_UNSET); 508 } 509 510 public synchronized void setOption(String option, String value)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 getOption(String option)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 isOpen()566 isOpen() 567 { 568 return ((state != State.INIT) && (state != State.CLOSED)); 569 } 570 571 public final synchronized boolean isEnabled()572 isEnabled() 573 { 574 if (state != State.COMPILED) { 575 return false; 576 } 577 578 return _isEnabled(); 579 } 580 581 public final synchronized boolean isRunning()582 isRunning() 583 { 584 return (state == State.STARTED); 585 } 586 587 public final synchronized boolean isClosed()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 work()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 if (state == State.STOPPED || state == State.CLOSED) { 639 // 640 // This consumer was stopped just after calling 641 // go() but before starting (the premature return 642 // case at the top of this work() method). It is 643 // possible to call close() on a consumer that has 644 // been stopped before starting. In that case the 645 // premature return above still takes us here in the 646 // finally clause, and we must not revert the CLOSED 647 // state to STOPPED. 648 // 649 } else { 650 state = State.STOPPED; 651 fireConsumerStopped(new ConsumerEvent(this, 652 System.nanoTime())); 653 } 654 } 655 656 // Notify the stop() method to stop waiting 657 workEnded = true; 658 stopLock.notifyAll(); 659 } 660 } 661 } 662 663 /** 664 * Creates the background thread started by {@link #go()} to run 665 * this consumer. Override this method if you need to set 666 * non-default {@code Thread} options or create the thread in a 667 * {@code ThreadGroup}. If you don't need to create the thread 668 * yourself, set the desired options on {@code super.createThread()} 669 * before returning it. Otherwise, the {@code Runnable} target of 670 * the created thread must call {@link #work()} in order to run this 671 * DTrace consumer. For example, to modify the default background 672 * consumer thread: 673 * <pre><code> 674 * protected Thread 675 * createThread() 676 * { 677 * Thread t = super.createThread(); 678 * t.setPriority(Thread.MIN_PRIORITY); 679 * return t; 680 * } 681 * </code></pre> 682 * Or if you need to create your own thread: 683 * <pre></code> 684 * protected Thread 685 * createThread() 686 * { 687 * Runnable target = new Runnable() { 688 * public void run() { 689 * work(); 690 * } 691 * }; 692 * String name = "Consumer " + UserApplication.sequence++; 693 * Thread t = new Thread(UserApplication.threadGroup, 694 * target, name); 695 * return t; 696 * } 697 * </code></pre> 698 * Do not start the returned thread, otherwise {@code go()} will 699 * throw an {@link IllegalThreadStateException} when it tries to 700 * start the returned thread a second time. 701 */ 702 protected Thread createThread()703 createThread() 704 { 705 Thread t = new Thread(new Runnable() { 706 public void run() { 707 work(); 708 } 709 }, "DTrace consumer " + id); 710 return t; 711 } 712 713 /** 714 * @inheritDoc 715 * @throws IllegalThreadStateException if a subclass calls {@link 716 * Thread#start()} on the value of {@link #createThread()} 717 * @see #createThread() 718 */ 719 public void go()720 go() throws DTraceException 721 { 722 go(null); 723 } 724 725 /** 726 * @inheritDoc 727 * @throws IllegalThreadStateException if a subclass calls {@link 728 * Thread#start()} on the value of {@link #createThread()} 729 * @see #createThread() 730 */ 731 public synchronized void go(ExceptionHandler h)732 go(ExceptionHandler h) throws DTraceException 733 { 734 switch (state) { 735 case INIT: 736 throw new IllegalStateException("consumer not open"); 737 case OPEN: 738 throw new IllegalStateException("no compiled program"); 739 case COMPILED: 740 // 741 // Throws IllegalStateException if not all compiled programs are 742 // also enabled. Does not make any calls to libdtrace. 743 // 744 _checkProgramEnabling(); 745 break; 746 case GO: 747 case STARTED: 748 throw new IllegalStateException("go() already called"); 749 case STOPPED: 750 throw new IllegalStateException("consumer stopped"); 751 case CLOSED: 752 throw new IllegalStateException("consumer closed"); 753 default: 754 throw new IllegalArgumentException("unknown state: " + state); 755 } 756 757 synchronized (LocalConsumer.class) { 758 _go(); 759 } 760 761 state = State.GO; 762 exceptionHandler = h; 763 Thread t = createThread(); 764 t.start(); 765 } 766 767 /** 768 * @inheritDoc 769 * 770 * @throws IllegalThreadStateException if attempting to {@code 771 * stop()} a running consumer while holding the lock on that 772 * consumer 773 */ 774 public void stop()775 stop() 776 { 777 boolean running = false; 778 779 synchronized (this) { 780 switch (state) { 781 case INIT: 782 throw new IllegalStateException("consumer not open"); 783 case OPEN: 784 case COMPILED: 785 throw new IllegalStateException("go() not called"); 786 case GO: 787 try { 788 synchronized (LocalConsumer.class) { 789 _stop(); 790 } 791 state = State.STOPPED; 792 fireConsumerStopped(new ConsumerEvent(this, 793 System.nanoTime())); 794 } catch (DTraceException e) { 795 if (exceptionHandler != null) { 796 exceptionHandler.handleException(e); 797 } else { 798 e.printStackTrace(); 799 } 800 } 801 break; 802 case STARTED: 803 running = true; 804 break; 805 case STOPPED: 806 // 807 // The work() thread that runs the native consumer 808 // loop may have terminated because of the exit() 809 // action in a DTrace program. In that case, a 810 // RuntimeException is inappropriate because there 811 // is no misuse of the API. Creating a new checked 812 // exception type to handle this case seems to offer 813 // no benefit for the trouble to the caller. 814 // Instead, the situation calls for stop() to be 815 // quietly tolerant. 816 // 817 if (stopCalled) { 818 throw new IllegalStateException( 819 "consumer already stopped"); 820 } 821 logger.fine("consumer already stopped"); 822 break; 823 case CLOSED: 824 throw new IllegalStateException("consumer closed"); 825 default: 826 throw new IllegalArgumentException("unknown state: " + 827 state); 828 } 829 830 stopCalled = true; 831 } 832 833 if (running) { 834 if (Thread.holdsLock(this)) { 835 throw new IllegalThreadStateException("The current " + 836 "thread cannot stop this LocalConsumer while " + 837 "holding the lock on this LocalConsumer"); 838 } 839 840 // 841 // Calls no libdtrace methods, so no synchronization is 842 // needed. Sets a native flag that causes the consumer 843 // thread to exit the consumer loop and call native 844 // dtrace_stop() at the end of the current interval (after 845 // grabbing the global Consumer.class lock required for any 846 // libdtrace call). 847 // 848 _interrupt(); 849 850 synchronized (stopLock) { 851 // 852 // Wait for work() to set workEnded. If the work() 853 // thread got the stopLock first, then workEnded is 854 // already set. 855 // 856 while (!workEnded) { 857 try { 858 stopLock.wait(); 859 } catch (InterruptedException e) { 860 logger.warning(e.toString()); 861 // do nothing but re-check the condition for 862 // waiting 863 } 864 } 865 } 866 } 867 } 868 869 public synchronized void abort()870 abort() 871 { 872 if ((state != State.INIT) && (state != State.CLOSED)) { 873 _interrupt(); 874 } 875 abortCalled = true; 876 } 877 878 /** 879 * @inheritDoc 880 * 881 * @throws IllegalThreadStateException if attempting to {@code 882 * close()} a running consumer while holding the lock on that 883 * consumer 884 */ 885 public void close()886 close() 887 { 888 synchronized (this) { 889 if ((state == State.INIT) || (state == State.CLOSED)) { 890 state = State.CLOSED; 891 return; 892 } 893 } 894 895 try { 896 stop(); 897 } catch (IllegalStateException e) { 898 // ignore (we don't have synchronized state access because 899 // it is illegal to call stop() while holding the lock on 900 // this consumer) 901 } 902 903 synchronized (this) { 904 if (state != State.CLOSED) { 905 synchronized (LocalConsumer.class) { 906 _close(); 907 } 908 _destroy(); 909 state = State.CLOSED; 910 911 if (logger.isLoggable(Level.INFO)) { 912 logger.info("consumer table count: " + _openCount()); 913 } 914 } 915 } 916 } 917 918 public void addConsumerListener(ConsumerListener l)919 addConsumerListener(ConsumerListener l) 920 { 921 listenerList.add(ConsumerListener.class, l); 922 } 923 924 public void removeConsumerListener(ConsumerListener l)925 removeConsumerListener(ConsumerListener l) 926 { 927 listenerList.remove(ConsumerListener.class, l); 928 } 929 930 public Aggregate getAggregate()931 getAggregate() throws DTraceException 932 { 933 // include all, clear none 934 return getAggregate(null, Collections. <String> emptySet()); 935 } 936 937 public Aggregate getAggregate(Set <String> includedAggregationNames)938 getAggregate(Set <String> includedAggregationNames) 939 throws DTraceException 940 { 941 return getAggregate(includedAggregationNames, 942 Collections. <String> emptySet()); 943 } 944 945 public Aggregate getAggregate(Set <String> includedAggregationNames, Set <String> clearedAggregationNames)946 getAggregate(Set <String> includedAggregationNames, 947 Set <String> clearedAggregationNames) 948 throws DTraceException 949 { 950 AggregateSpec spec = new AggregateSpec(); 951 952 if (includedAggregationNames == null) { 953 spec.setIncludeByDefault(true); 954 } else { 955 spec.setIncludeByDefault(false); 956 for (String included : includedAggregationNames) { 957 spec.addIncludedAggregationName(included); 958 } 959 } 960 961 if (clearedAggregationNames == null) { 962 spec.setClearByDefault(true); 963 } else { 964 spec.setClearByDefault(false); 965 for (String cleared : clearedAggregationNames) { 966 spec.addClearedAggregationName(cleared); 967 } 968 } 969 970 return getAggregate(spec); 971 } 972 973 private synchronized Aggregate getAggregate(AggregateSpec spec)974 getAggregate(AggregateSpec spec) throws DTraceException 975 { 976 // 977 // It should be possible to request aggregation data after a 978 // consumer has stopped but not after it has been closed. 979 // 980 checkGoCalled(); 981 982 // 983 // Getting the aggregate is a time-consuming request that should not 984 // prevent other consumers from running concurrently. Instead, 985 // native code will acquire the LocalConsumer.class monitor as 986 // needed before calling libdtrace functions. 987 // 988 Aggregate aggregate = _getAggregate(spec); 989 return aggregate; 990 } 991 992 private synchronized void checkGoCalled()993 checkGoCalled() 994 { 995 switch (state) { 996 case INIT: 997 throw new IllegalStateException("consumer not open"); 998 case OPEN: 999 case COMPILED: 1000 throw new IllegalStateException("go() not called"); 1001 case GO: 1002 case STARTED: 1003 case STOPPED: 1004 break; 1005 case CLOSED: 1006 throw new IllegalStateException("consumer closed"); 1007 } 1008 } 1009 1010 private synchronized void checkGoNotCalled()1011 checkGoNotCalled() 1012 { 1013 switch (state) { 1014 case INIT: 1015 throw new IllegalStateException("consumer not open"); 1016 case OPEN: 1017 case COMPILED: 1018 break; 1019 case GO: 1020 case STARTED: 1021 throw new IllegalStateException("go() already called"); 1022 case STOPPED: 1023 throw new IllegalStateException("consumer stopped"); 1024 case CLOSED: 1025 throw new IllegalStateException("consumer closed"); 1026 } 1027 } 1028 1029 public synchronized int createProcess(String command)1030 createProcess(String command) throws DTraceException 1031 { 1032 if (command == null) { 1033 throw new NullPointerException("command is null"); 1034 } 1035 1036 checkGoNotCalled(); 1037 1038 int pid; 1039 synchronized (LocalConsumer.class) { 1040 pid = _createProcess(command); 1041 } 1042 return pid; 1043 } 1044 1045 public synchronized void grabProcess(int pid)1046 grabProcess(int pid) throws DTraceException 1047 { 1048 checkGoNotCalled(); 1049 1050 synchronized (LocalConsumer.class) { 1051 _grabProcess(pid); 1052 } 1053 } 1054 1055 public synchronized List <ProbeDescription> listProbes(ProbeDescription filter)1056 listProbes(ProbeDescription filter) throws DTraceException 1057 { 1058 checkGoNotCalled(); 1059 List <ProbeDescription> probeList = 1060 new LinkedList <ProbeDescription> (); 1061 if (filter == ProbeDescription.EMPTY) { 1062 filter = null; 1063 } 1064 synchronized (LocalConsumer.class) { 1065 _listProbes(probeList, filter); 1066 } 1067 return probeList; 1068 } 1069 1070 public synchronized List <Probe> listProbeDetail(ProbeDescription filter)1071 listProbeDetail(ProbeDescription filter) throws DTraceException 1072 { 1073 checkGoNotCalled(); 1074 List <Probe> probeList = new LinkedList <Probe> (); 1075 if (filter == ProbeDescription.EMPTY) { 1076 filter = null; 1077 } 1078 synchronized (LocalConsumer.class) { 1079 _listProbeDetail(probeList, filter); 1080 } 1081 return probeList; 1082 } 1083 1084 public synchronized List <ProbeDescription> listProgramProbes(Program program)1085 listProgramProbes(Program program) throws DTraceException 1086 { 1087 checkProgram(program); 1088 checkGoNotCalled(); 1089 List <ProbeDescription> probeList = 1090 new LinkedList <ProbeDescription> (); 1091 synchronized (LocalConsumer.class) { 1092 _listCompiledProbes(probeList, program); 1093 } 1094 return probeList; 1095 } 1096 1097 public synchronized List <Probe> listProgramProbeDetail(Program program)1098 listProgramProbeDetail(Program program) throws DTraceException 1099 { 1100 checkProgram(program); 1101 checkGoNotCalled(); 1102 List <Probe> probeList = new LinkedList <Probe> (); 1103 synchronized (LocalConsumer.class) { 1104 _listCompiledProbeDetail(probeList, program); 1105 } 1106 return probeList; 1107 } 1108 1109 public synchronized String lookupKernelFunction(int address)1110 lookupKernelFunction(int address) 1111 { 1112 checkGoCalled(); 1113 synchronized (LocalConsumer.class) { 1114 return _lookupKernelFunction(new Integer(address)); 1115 } 1116 } 1117 1118 public synchronized String lookupKernelFunction(long address)1119 lookupKernelFunction(long address) 1120 { 1121 checkGoCalled(); 1122 synchronized (LocalConsumer.class) { 1123 return _lookupKernelFunction(new Long(address)); 1124 } 1125 } 1126 1127 public synchronized String lookupUserFunction(int pid, int address)1128 lookupUserFunction(int pid, int address) 1129 { 1130 checkGoCalled(); 1131 synchronized (LocalConsumer.class) { 1132 return _lookupUserFunction(pid, new Integer(address)); 1133 } 1134 } 1135 1136 public synchronized String lookupUserFunction(int pid, long address)1137 lookupUserFunction(int pid, long address) 1138 { 1139 checkGoCalled(); 1140 synchronized (LocalConsumer.class) { 1141 return _lookupUserFunction(pid, new Long(address)); 1142 } 1143 } 1144 1145 public String getVersion()1146 getVersion() 1147 { 1148 synchronized (LocalConsumer.class) { 1149 return LocalConsumer._getVersion(); 1150 } 1151 } 1152 1153 /** 1154 * Called by native code. 1155 */ 1156 private void nextProbeData(ProbeData probeData)1157 nextProbeData(ProbeData probeData) throws ConsumerException 1158 { 1159 fireDataReceived(new DataEvent(this, probeData)); 1160 } 1161 1162 /** 1163 * Called by native code. 1164 */ 1165 private void dataDropped(Drop drop)1166 dataDropped(Drop drop) throws ConsumerException 1167 { 1168 fireDataDropped(new DropEvent(this, drop)); 1169 } 1170 1171 /** 1172 * Called by native code. 1173 */ 1174 private void errorEncountered(Error error)1175 errorEncountered(Error error) throws ConsumerException 1176 { 1177 fireErrorEncountered(new ErrorEvent(this, error)); 1178 } 1179 1180 /** 1181 * Called by native code. 1182 */ 1183 private void processStateChanged(ProcessState processState)1184 processStateChanged(ProcessState processState) throws ConsumerException 1185 { 1186 fireProcessStateChanged(new ProcessEvent(this, processState)); 1187 } 1188 1189 protected void fireDataReceived(DataEvent e)1190 fireDataReceived(DataEvent e) throws ConsumerException 1191 { 1192 // Guaranteed to return a non-null array 1193 Object[] listeners = listenerList.getListenerList(); 1194 // Process the listeners last to first, notifying 1195 // those that are interested in this event 1196 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1197 if (listeners[i] == ConsumerListener.class) { 1198 ((ConsumerListener)listeners[i + 1]).dataReceived(e); 1199 } 1200 } 1201 } 1202 1203 protected void fireDataDropped(DropEvent e)1204 fireDataDropped(DropEvent e) throws ConsumerException 1205 { 1206 // Guaranteed to return a non-null array 1207 Object[] listeners = listenerList.getListenerList(); 1208 // Process the listeners last to first, notifying 1209 // those that are interested in this event 1210 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1211 if (listeners[i] == ConsumerListener.class) { 1212 ((ConsumerListener)listeners[i + 1]).dataDropped(e); 1213 } 1214 } 1215 } 1216 1217 protected void fireErrorEncountered(ErrorEvent e)1218 fireErrorEncountered(ErrorEvent e) throws ConsumerException 1219 { 1220 // Guaranteed to return a non-null array 1221 Object[] listeners = listenerList.getListenerList(); 1222 // Process the listeners last to first, notifying 1223 // those that are interested in this event 1224 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1225 if (listeners[i] == ConsumerListener.class) { 1226 ((ConsumerListener)listeners[i + 1]).errorEncountered(e); 1227 } 1228 } 1229 } 1230 1231 protected void fireProcessStateChanged(ProcessEvent e)1232 fireProcessStateChanged(ProcessEvent e) throws ConsumerException 1233 { 1234 // Guaranteed to return a non-null array 1235 Object[] listeners = listenerList.getListenerList(); 1236 // Process the listeners last to first, notifying 1237 // those that are interested in this event 1238 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1239 if (listeners[i] == ConsumerListener.class) { 1240 ((ConsumerListener)listeners[i + 1]).processStateChanged(e); 1241 } 1242 } 1243 } 1244 1245 protected void fireConsumerStarted(ConsumerEvent e)1246 fireConsumerStarted(ConsumerEvent e) 1247 { 1248 // Guaranteed to return a non-null array 1249 Object[] listeners = listenerList.getListenerList(); 1250 // Process the listeners last to first, notifying 1251 // those that are interested in this event 1252 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1253 if (listeners[i] == ConsumerListener.class) { 1254 ((ConsumerListener)listeners[i + 1]).consumerStarted(e); 1255 } 1256 } 1257 } 1258 1259 protected void fireConsumerStopped(ConsumerEvent e)1260 fireConsumerStopped(ConsumerEvent e) 1261 { 1262 // Guaranteed to return a non-null array 1263 Object[] listeners = listenerList.getListenerList(); 1264 // Process the listeners last to first, notifying 1265 // those that are interested in this event 1266 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1267 if (listeners[i] == ConsumerListener.class) { 1268 ((ConsumerListener)listeners[i + 1]).consumerStopped(e); 1269 } 1270 } 1271 } 1272 1273 // Called by native code 1274 private void intervalBegan()1275 intervalBegan() 1276 { 1277 fireIntervalBegan(new ConsumerEvent(this, System.nanoTime())); 1278 } 1279 1280 protected void fireIntervalBegan(ConsumerEvent e)1281 fireIntervalBegan(ConsumerEvent e) 1282 { 1283 // Guaranteed to return a non-null array 1284 Object[] listeners = listenerList.getListenerList(); 1285 // Process the listeners last to first, notifying 1286 // those that are interested in this event 1287 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1288 if (listeners[i] == ConsumerListener.class) { 1289 ((ConsumerListener)listeners[i + 1]).intervalBegan(e); 1290 } 1291 } 1292 } 1293 1294 // Called by native code 1295 private void intervalEnded()1296 intervalEnded() 1297 { 1298 fireIntervalEnded(new ConsumerEvent(this, System.nanoTime())); 1299 } 1300 1301 protected void fireIntervalEnded(ConsumerEvent e)1302 fireIntervalEnded(ConsumerEvent e) 1303 { 1304 // Guaranteed to return a non-null array 1305 Object[] listeners = listenerList.getListenerList(); 1306 // Process the listeners last to first, notifying 1307 // those that are interested in this event 1308 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1309 if (listeners[i] == ConsumerListener.class) { 1310 ((ConsumerListener)listeners[i + 1]).intervalEnded(e); 1311 } 1312 } 1313 } 1314 1315 /** 1316 * Gets a string representation of this consumer useful for logging 1317 * and not intended for display. The exact details of the 1318 * representation are unspecified and subject to change, but the 1319 * following format may be regarded as typical: 1320 * <pre><code> 1321 * class-name[property1 = value1, property2 = value2] 1322 * </code></pre> 1323 */ 1324 public String toString()1325 toString() 1326 { 1327 StringBuilder buf = new StringBuilder(LocalConsumer.class.getName()); 1328 synchronized (this) { 1329 buf.append("[open = "); 1330 buf.append(isOpen()); 1331 buf.append(", enabled = "); 1332 buf.append(isEnabled()); 1333 buf.append(", running = "); 1334 buf.append(isRunning()); 1335 buf.append(", closed = "); 1336 buf.append(isClosed()); 1337 } 1338 buf.append(']'); 1339 return buf.toString(); 1340 } 1341 1342 /** 1343 * Ensures that the {@link #close()} method of this consumer has 1344 * been called before it is garbage-collected. The intended safety 1345 * net is weak because the JVM does not guarantee that an object 1346 * will be garbage-collected when it is no longer referenced. Users 1347 * of the API should call {@code close()} to ensure that all 1348 * resources associated with this consumer are reclaimed in a timely 1349 * manner. 1350 * 1351 * @see #close() 1352 */ 1353 protected void finalize()1354 finalize() 1355 { 1356 close(); 1357 } 1358 1359 private String getTag()1360 getTag() 1361 { 1362 return super.toString(); 1363 } 1364 1365 // 1366 // Uniquely identifies a consumer across systems so it is possible 1367 // to validate that an object such as a Program passed to a remote 1368 // client over a socket was created by this consumer and no other. 1369 // 1370 static class Identifier implements Serializable { 1371 static final long serialVersionUID = 2183165132305302834L; 1372 1373 // local identifier 1374 private int id; 1375 private long timestamp; 1376 // remote identifier 1377 private InetAddress localHost; 1378 private String tag; // in case localHost not available 1379 1380 private Identifier(LocalConsumer consumer)1381 Identifier(LocalConsumer consumer) 1382 { 1383 id = LocalConsumer.sequence++; 1384 timestamp = System.currentTimeMillis(); 1385 try { 1386 localHost = InetAddress.getLocalHost(); 1387 } catch (UnknownHostException e) { 1388 localHost = null; 1389 } 1390 tag = consumer.getTag(); 1391 } 1392 1393 @Override 1394 public boolean equals(Object o)1395 equals(Object o) 1396 { 1397 if (o == this) { 1398 return true; 1399 } 1400 if (o instanceof Identifier) { 1401 Identifier i = (Identifier)o; 1402 return ((id == i.id) && 1403 (timestamp == i.timestamp) && 1404 ((localHost == null) ? (i.localHost == null) : 1405 localHost.equals(i.localHost)) && 1406 tag.equals(i.tag)); 1407 } 1408 return false; 1409 } 1410 1411 @Override 1412 public int hashCode()1413 hashCode() 1414 { 1415 int hash = 17; 1416 hash = (37 * hash) + id; 1417 hash = (37 * hash) + ((int)(timestamp ^ (timestamp >>> 32))); 1418 hash = (37 * hash) + (localHost == null ? 0 : 1419 localHost.hashCode()); 1420 hash = (37 * hash) + tag.hashCode(); 1421 return hash; 1422 } 1423 1424 @Override 1425 public String toString()1426 toString() 1427 { 1428 StringBuilder buf = new StringBuilder(); 1429 buf.append(Identifier.class.getName()); 1430 buf.append("[id = "); 1431 buf.append(id); 1432 buf.append(", timestamp = "); 1433 buf.append(timestamp); 1434 buf.append(", localHost = "); 1435 buf.append(localHost); 1436 buf.append(", tag = "); 1437 buf.append(tag); 1438 buf.append(']'); 1439 return buf.toString(); 1440 } 1441 } 1442 } 1443