1 /*
   2  * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.management;
  27 
  28 import javax.management.openmbean.CompositeData;
  29 import sun.management.ManagementFactoryHelper;
  30 import sun.management.ThreadInfoCompositeData;
  31 import static java.lang.Thread.State.*;
  32 
  33 /**
  34  * Thread information. <tt>ThreadInfo</tt> contains the information
  35  * about a thread including:
  36  * <h4>General thread information</h4>
  37  * <ul>
  38  *   <li>Thread ID.</li>
  39  *   <li>Name of the thread.</li>
  40  * </ul>
  41  *
  42  * <h4>Execution information</h4>
  43  * <ul>
  44  *   <li>Thread state.</li>
  45  *   <li>The object upon which the thread is blocked due to:
  46  *       <ul>
  47  *       <li>waiting to enter a synchronization block/method, or</li>
  48  *       <li>waiting to be notified in a {@link Object#wait Object.wait} method,
  49  *           or</li>
  50  *       <li>parking due to a {@link java.util.concurrent.locks.LockSupport#park
  51  *           LockSupport.park} call.</li>
  52  *       </ul>
  53  *   </li>
  54  *   <li>The ID of the thread that owns the object
  55  *       that the thread is blocked.</li>
  56  *   <li>Stack trace of the thread.</li>
  57  *   <li>List of object monitors locked by the thread.</li>
  58  *   <li>List of <a href="LockInfo.html#OwnableSynchronizer">
  59  *       ownable synchronizers</a> locked by the thread.</li>
  60  * </ul>
  61  *
  62  * <h4><a name="SyncStats">Synchronization Statistics</a></h4>
  63  * <ul>
  64  *   <li>The number of times that the thread has blocked for
  65  *       synchronization or waited for notification.</li>
  66  *   <li>The accumulated elapsed time that the thread has blocked
  67  *       for synchronization or waited for notification
  68  *       since {@link ThreadMXBean#setThreadContentionMonitoringEnabled
  69  *       thread contention monitoring}
  70  *       was enabled. Some Java virtual machine implementation
  71  *       may not support this.  The
  72  *       {@link ThreadMXBean#isThreadContentionMonitoringSupported()}
  73  *       method can be used to determine if a Java virtual machine
  74  *       supports this.</li>
  75  * </ul>
  76  *
  77  * <p>This thread information class is designed for use in monitoring of
  78  * the system, not for synchronization control.
  79  *
  80  * <h4>MXBean Mapping</h4>
  81  * <tt>ThreadInfo</tt> is mapped to a {@link CompositeData CompositeData}
  82  * with attributes as specified in
  83  * the {@link #from from} method.
  84  *
  85  * @see ThreadMXBean#getThreadInfo
  86  * @see ThreadMXBean#dumpAllThreads
  87  *
  88  * @author  Mandy Chung
  89  * @since   1.5
  90  */
  91 
  92 public class ThreadInfo {
  93     private String       threadName;
  94     private long         threadId;
  95     private long         blockedTime;
  96     private long         blockedCount;
  97     private long         waitedTime;
  98     private long         waitedCount;
  99     private LockInfo     lock;
 100     private String       lockName;
 101     private long         lockOwnerId;
 102     private String       lockOwnerName;
 103     private boolean      inNative;
 104     private boolean      suspended;
 105     private Thread.State threadState;
 106     private StackTraceElement[] stackTrace;
 107     private MonitorInfo[]       lockedMonitors;
 108     private LockInfo[]          lockedSynchronizers;
 109 
 110     private static MonitorInfo[] EMPTY_MONITORS = new MonitorInfo[0];
 111     private static LockInfo[] EMPTY_SYNCS = new LockInfo[0];
 112 
 113     /**
 114      * Constructor of ThreadInfo created by the JVM
 115      *
 116      * @param t             Thread
 117      * @param state         Thread state
 118      * @param lockObj       Object on which the thread is blocked
 119      * @param lockOwner     the thread holding the lock
 120      * @param blockedCount  Number of times blocked to enter a lock
 121      * @param blockedTime   Approx time blocked to enter a lock
 122      * @param waitedCount   Number of times waited on a lock
 123      * @param waitedTime    Approx time waited on a lock
 124      * @param stackTrace    Thread stack trace
 125      */
 126     private ThreadInfo(Thread t, int state, Object lockObj, Thread lockOwner,
 127                        long blockedCount, long blockedTime,
 128                        long waitedCount, long waitedTime,
 129                        StackTraceElement[] stackTrace) {
 130         initialize(t, state, lockObj, lockOwner,
 131                    blockedCount, blockedTime,
 132                    waitedCount, waitedTime, stackTrace,
 133                    EMPTY_MONITORS, EMPTY_SYNCS);
 134     }
 135 
 136     /**
 137      * Constructor of ThreadInfo created by the JVM
 138      * for {@link ThreadMXBean#getThreadInfo(long[],boolean,boolean)}
 139      * and {@link ThreadMXBean#dumpAllThreads}
 140      *
 141      * @param t             Thread
 142      * @param state         Thread state
 143      * @param lockObj       Object on which the thread is blocked
 144      * @param lockOwner     the thread holding the lock
 145      * @param blockedCount  Number of times blocked to enter a lock
 146      * @param blockedTime   Approx time blocked to enter a lock
 147      * @param waitedCount   Number of times waited on a lock
 148      * @param waitedTime    Approx time waited on a lock
 149      * @param stackTrace    Thread stack trace
 150      * @param monitors      List of locked monitors
 151      * @param stackDepths   List of stack depths
 152      * @param synchronizers List of locked synchronizers
 153      */
 154     private ThreadInfo(Thread t, int state, Object lockObj, Thread lockOwner,
 155                        long blockedCount, long blockedTime,
 156                        long waitedCount, long waitedTime,
 157                        StackTraceElement[] stackTrace,
 158                        Object[] monitors,
 159                        int[] stackDepths,
 160                        Object[] synchronizers) {
 161         int numMonitors = (monitors == null ? 0 : monitors.length);
 162         MonitorInfo[] lockedMonitors;
 163         if (numMonitors == 0) {
 164             lockedMonitors = EMPTY_MONITORS;
 165         } else {
 166             lockedMonitors = new MonitorInfo[numMonitors];
 167             for (int i = 0; i < numMonitors; i++) {
 168                 Object lock = monitors[i];
 169                 String className = lock.getClass().getName();
 170                 int identityHashCode = System.identityHashCode(lock);
 171                 int depth = stackDepths[i];
 172                 StackTraceElement ste = (depth >= 0 ? stackTrace[depth]
 173                                                     : null);
 174                 lockedMonitors[i] = new MonitorInfo(className,
 175                                                     identityHashCode,
 176                                                     depth,
 177                                                     ste);
 178             }
 179         }
 180 
 181         int numSyncs = (synchronizers == null ? 0 : synchronizers.length);
 182         LockInfo[] lockedSynchronizers;
 183         if (numSyncs == 0) {
 184             lockedSynchronizers = EMPTY_SYNCS;
 185         } else {
 186             lockedSynchronizers = new LockInfo[numSyncs];
 187             for (int i = 0; i < numSyncs; i++) {
 188                 Object lock = synchronizers[i];
 189                 String className = lock.getClass().getName();
 190                 int identityHashCode = System.identityHashCode(lock);
 191                 lockedSynchronizers[i] = new LockInfo(className,
 192                                                       identityHashCode);
 193             }
 194         }
 195 
 196         initialize(t, state, lockObj, lockOwner,
 197                    blockedCount, blockedTime,
 198                    waitedCount, waitedTime, stackTrace,
 199                    lockedMonitors, lockedSynchronizers);
 200     }
 201 
 202     /**
 203      * Initialize ThreadInfo object
 204      *
 205      * @param t             Thread
 206      * @param state         Thread state
 207      * @param lockObj       Object on which the thread is blocked
 208      * @param lockOwner     the thread holding the lock
 209      * @param blockedCount  Number of times blocked to enter a lock
 210      * @param blockedTime   Approx time blocked to enter a lock
 211      * @param waitedCount   Number of times waited on a lock
 212      * @param waitedTime    Approx time waited on a lock
 213      * @param stackTrace    Thread stack trace
 214      * @param lockedMonitors List of locked monitors
 215      * @param lockedSynchronizers List of locked synchronizers
 216      */
 217     private void initialize(Thread t, int state, Object lockObj, Thread lockOwner,
 218                             long blockedCount, long blockedTime,
 219                             long waitedCount, long waitedTime,
 220                             StackTraceElement[] stackTrace,
 221                             MonitorInfo[] lockedMonitors,
 222                             LockInfo[] lockedSynchronizers) {
 223         this.threadId = t.getId();
 224         this.threadName = t.getName();
 225         this.threadState = ManagementFactoryHelper.toThreadState(state);
 226         this.suspended = ManagementFactoryHelper.isThreadSuspended(state);
 227         this.inNative = ManagementFactoryHelper.isThreadRunningNative(state);
 228         this.blockedCount = blockedCount;
 229         this.blockedTime = blockedTime;
 230         this.waitedCount = waitedCount;
 231         this.waitedTime = waitedTime;
 232 
 233         if (lockObj == null) {
 234             this.lock = null;
 235             this.lockName = null;
 236         } else {
 237             this.lock = new LockInfo(lockObj);
 238             this.lockName =
 239                 lock.getClassName() + '@' +
 240                     Integer.toHexString(lock.getIdentityHashCode());
 241         }
 242         if (lockOwner == null) {
 243             this.lockOwnerId = -1;
 244             this.lockOwnerName = null;
 245         } else {
 246             this.lockOwnerId = lockOwner.getId();
 247             this.lockOwnerName = lockOwner.getName();
 248         }
 249         if (stackTrace == null) {
 250             this.stackTrace = NO_STACK_TRACE;
 251         } else {
 252             this.stackTrace = stackTrace;
 253         }
 254         this.lockedMonitors = lockedMonitors;
 255         this.lockedSynchronizers = lockedSynchronizers;
 256     }
 257 
 258     /*
 259      * Constructs a <tt>ThreadInfo</tt> object from a
 260      * {@link CompositeData CompositeData}.
 261      */
 262     private ThreadInfo(CompositeData cd) {
 263         ThreadInfoCompositeData ticd = ThreadInfoCompositeData.getInstance(cd);
 264 
 265         threadId = ticd.threadId();
 266         threadName = ticd.threadName();
 267         blockedTime = ticd.blockedTime();
 268         blockedCount = ticd.blockedCount();
 269         waitedTime = ticd.waitedTime();
 270         waitedCount = ticd.waitedCount();
 271         lockName = ticd.lockName();
 272         lockOwnerId = ticd.lockOwnerId();
 273         lockOwnerName = ticd.lockOwnerName();
 274         threadState = ticd.threadState();
 275         suspended = ticd.suspended();
 276         inNative = ticd.inNative();
 277         stackTrace = ticd.stackTrace();
 278 
 279         // 6.0 attributes
 280         if (ticd.isCurrentVersion()) {
 281             lock = ticd.lockInfo();
 282             lockedMonitors = ticd.lockedMonitors();
 283             lockedSynchronizers = ticd.lockedSynchronizers();
 284         } else {
 285             // lockInfo is a new attribute added in 1.6 ThreadInfo
 286             // If cd is a 5.0 version, construct the LockInfo object
 287             //  from the lockName value.
 288             if (lockName != null) {
 289                 String result[] = lockName.split("@");
 290                 if (result.length == 2) {
 291                     int identityHashCode = Integer.parseInt(result[1], 16);
 292                     lock = new LockInfo(result[0], identityHashCode);
 293                 } else {
 294                     assert result.length == 2;
 295                     lock = null;
 296                 }
 297             } else {
 298                 lock = null;
 299             }
 300             lockedMonitors = EMPTY_MONITORS;
 301             lockedSynchronizers = EMPTY_SYNCS;
 302         }
 303     }
 304 
 305     /**
 306      * Returns the ID of the thread associated with this <tt>ThreadInfo</tt>.
 307      *
 308      * @return the ID of the associated thread.
 309      */
 310     public long getThreadId() {
 311         return threadId;
 312     }
 313 
 314     /**
 315      * Returns the name of the thread associated with this <tt>ThreadInfo</tt>.
 316      *
 317      * @return the name of the associated thread.
 318      */
 319     public String getThreadName() {
 320         return threadName;
 321     }
 322 
 323     /**
 324      * Returns the state of the thread associated with this <tt>ThreadInfo</tt>.
 325      *
 326      * @return <tt>Thread.State</tt> of the associated thread.
 327      */
 328     public Thread.State getThreadState() {
 329          return threadState;
 330     }
 331 
 332     /**
 333      * Returns the approximate accumulated elapsed time (in milliseconds)
 334      * that the thread associated with this <tt>ThreadInfo</tt>
 335      * has blocked to enter or reenter a monitor
 336      * since thread contention monitoring is enabled.
 337      * I.e. the total accumulated time the thread has been in the
 338      * {@link java.lang.Thread.State#BLOCKED BLOCKED} state since thread
 339      * contention monitoring was last enabled.
 340      * This method returns <tt>-1</tt> if thread contention monitoring
 341      * is disabled.
 342      *
 343      * <p>The Java virtual machine may measure the time with a high
 344      * resolution timer.  This statistic is reset when
 345      * the thread contention monitoring is reenabled.
 346      *
 347      * @return the approximate accumulated elapsed time in milliseconds
 348      * that a thread entered the <tt>BLOCKED</tt> state;
 349      * <tt>-1</tt> if thread contention monitoring is disabled.
 350      *
 351      * @throws java.lang.UnsupportedOperationException if the Java
 352      * virtual machine does not support this operation.
 353      *
 354      * @see ThreadMXBean#isThreadContentionMonitoringSupported
 355      * @see ThreadMXBean#setThreadContentionMonitoringEnabled
 356      */
 357     public long getBlockedTime() {
 358         return blockedTime;
 359     }
 360 
 361     /**
 362      * Returns the total number of times that
 363      * the thread associated with this <tt>ThreadInfo</tt>
 364      * blocked to enter or reenter a monitor.
 365      * I.e. the number of times a thread has been in the
 366      * {@link java.lang.Thread.State#BLOCKED BLOCKED} state.
 367      *
 368      * @return the total number of times that the thread
 369      * entered the <tt>BLOCKED</tt> state.
 370      */
 371     public long getBlockedCount() {
 372         return blockedCount;
 373     }
 374 
 375     /**
 376      * Returns the approximate accumulated elapsed time (in milliseconds)
 377      * that the thread associated with this <tt>ThreadInfo</tt>
 378      * has waited for notification
 379      * since thread contention monitoring is enabled.
 380      * I.e. the total accumulated time the thread has been in the
 381      * {@link java.lang.Thread.State#WAITING WAITING}
 382      * or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state
 383      * since thread contention monitoring is enabled.
 384      * This method returns <tt>-1</tt> if thread contention monitoring
 385      * is disabled.
 386      *
 387      * <p>The Java virtual machine may measure the time with a high
 388      * resolution timer.  This statistic is reset when
 389      * the thread contention monitoring is reenabled.
 390      *
 391      * @return the approximate accumulated elapsed time in milliseconds
 392      * that a thread has been in the <tt>WAITING</tt> or
 393      * <tt>TIMED_WAITING</tt> state;
 394      * <tt>-1</tt> if thread contention monitoring is disabled.
 395      *
 396      * @throws java.lang.UnsupportedOperationException if the Java
 397      * virtual machine does not support this operation.
 398      *
 399      * @see ThreadMXBean#isThreadContentionMonitoringSupported
 400      * @see ThreadMXBean#setThreadContentionMonitoringEnabled
 401      */
 402     public long getWaitedTime() {
 403         return waitedTime;
 404     }
 405 
 406     /**
 407      * Returns the total number of times that
 408      * the thread associated with this <tt>ThreadInfo</tt>
 409      * waited for notification.
 410      * I.e. the number of times that a thread has been
 411      * in the {@link java.lang.Thread.State#WAITING WAITING}
 412      * or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state.
 413      *
 414      * @return the total number of times that the thread
 415      * was in the <tt>WAITING</tt> or <tt>TIMED_WAITING</tt> state.
 416      */
 417     public long getWaitedCount() {
 418         return waitedCount;
 419     }
 420 
 421     /**
 422      * Returns the <tt>LockInfo</tt> of an object for which
 423      * the thread associated with this <tt>ThreadInfo</tt>
 424      * is blocked waiting.
 425      * A thread can be blocked waiting for one of the following:
 426      * <ul>
 427      * <li>an object monitor to be acquired for entering or reentering
 428      *     a synchronization block/method.
 429      *     <br>The thread is in the {@link java.lang.Thread.State#BLOCKED BLOCKED}
 430      *     state waiting to enter the <tt>synchronized</tt> statement
 431      *     or method.
 432      *     <p></li>
 433      * <li>an object monitor to be notified by another thread.
 434      *     <br>The thread is in the {@link java.lang.Thread.State#WAITING WAITING}
 435      *     or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state
 436      *     due to a call to the {@link Object#wait Object.wait} method.
 437      *     <p></li>
 438      * <li>a synchronization object responsible for the thread parking.
 439      *     <br>The thread is in the {@link java.lang.Thread.State#WAITING WAITING}
 440      *     or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state
 441      *     due to a call to the
 442      *     {@link java.util.concurrent.locks.LockSupport#park(Object)
 443      *     LockSupport.park} method.  The synchronization object
 444      *     is the object returned from
 445      *     {@link java.util.concurrent.locks.LockSupport#getBlocker
 446      *     LockSupport.getBlocker} method. Typically it is an
 447      *     <a href="LockInfo.html#OwnableSynchronizer"> ownable synchronizer</a>
 448      *     or a {@link java.util.concurrent.locks.Condition Condition}.</li>
 449      * </ul>
 450      *
 451      * <p>This method returns <tt>null</tt> if the thread is not in any of
 452      * the above conditions.
 453      *
 454      * @return <tt>LockInfo</tt> of an object for which the thread
 455      *         is blocked waiting if any; <tt>null</tt> otherwise.
 456      * @since 1.6
 457      */
 458     public LockInfo getLockInfo() {
 459         return lock;
 460     }
 461 
 462     /**
 463      * Returns the {@link LockInfo#toString string representation}
 464      * of an object for which the thread associated with this
 465      * <tt>ThreadInfo</tt> is blocked waiting.
 466      * This method is equivalent to calling:
 467      * <blockquote>
 468      * <pre>
 469      * getLockInfo().toString()
 470      * </pre></blockquote>
 471      *
 472      * <p>This method will return <tt>null</tt> if this thread is not blocked
 473      * waiting for any object or if the object is not owned by any thread.
 474      *
 475      * @return the string representation of the object on which
 476      * the thread is blocked if any;
 477      * <tt>null</tt> otherwise.
 478      *
 479      * @see #getLockInfo
 480      */
 481     public String getLockName() {
 482         return lockName;
 483     }
 484 
 485     /**
 486      * Returns the ID of the thread which owns the object
 487      * for which the thread associated with this <tt>ThreadInfo</tt>
 488      * is blocked waiting.
 489      * This method will return <tt>-1</tt> if this thread is not blocked
 490      * waiting for any object or if the object is not owned by any thread.
 491      *
 492      * @return the thread ID of the owner thread of the object
 493      * this thread is blocked on;
 494      * <tt>-1</tt> if this thread is not blocked
 495      * or if the object lis not owned by any thread.
 496      *
 497      * @see #getLockInfo
 498      */
 499     public long getLockOwnerId() {
 500         return lockOwnerId;
 501     }
 502 
 503     /**
 504      * Returns the name of the thread which owns the object
 505      * for which the thread associated with this <tt>ThreadInfo</tt>
 506      * is blocked waiting.
 507      * This method will return <tt>null</tt> if this thread is not blocked
 508      * waiting for any object or if the object is not owned by any thread.
 509      *
 510      * @return the name of the thread that owns the object
 511      * this thread is blocked on;
 512      * <tt>null</tt> if this thread is not blocked
 513      * or if the object is not owned by any thread.
 514      *
 515      * @see #getLockInfo
 516      */
 517     public String getLockOwnerName() {
 518         return lockOwnerName;
 519     }
 520 
 521     /**
 522      * Returns the stack trace of the thread
 523      * associated with this <tt>ThreadInfo</tt>.
 524      * If no stack trace was requested for this thread info, this method
 525      * will return a zero-length array.
 526      * If the returned array is of non-zero length then the first element of
 527      * the array represents the top of the stack, which is the most recent
 528      * method invocation in the sequence.  The last element of the array
 529      * represents the bottom of the stack, which is the least recent method
 530      * invocation in the sequence.
 531      *
 532      * <p>Some Java virtual machines may, under some circumstances, omit one
 533      * or more stack frames from the stack trace.  In the extreme case,
 534      * a virtual machine that has no stack trace information concerning
 535      * the thread associated with this <tt>ThreadInfo</tt>
 536      * is permitted to return a zero-length array from this method.
 537      *
 538      * @return an array of <tt>StackTraceElement</tt> objects of the thread.
 539      */
 540     public StackTraceElement[] getStackTrace() {
 541         return stackTrace;
 542     }
 543 
 544     /**
 545      * Tests if the thread associated with this <tt>ThreadInfo</tt>
 546      * is suspended.  This method returns <tt>true</tt> if
 547      * {@link Thread#suspend} has been called.
 548      *
 549      * @return <tt>true</tt> if the thread is suspended;
 550      *         <tt>false</tt> otherwise.
 551      */
 552     public boolean isSuspended() {
 553          return suspended;
 554     }
 555 
 556     /**
 557      * Tests if the thread associated with this <tt>ThreadInfo</tt>
 558      * is executing native code via the Java Native Interface (JNI).
 559      * The JNI native code does not include
 560      * the virtual machine support code or the compiled native
 561      * code generated by the virtual machine.
 562      *
 563      * @return <tt>true</tt> if the thread is executing native code;
 564      *         <tt>false</tt> otherwise.
 565      */
 566     public boolean isInNative() {
 567          return inNative;
 568     }
 569 
 570     /**
 571      * Returns a string representation of this thread info.
 572      * The format of this string depends on the implementation.
 573      * The returned string will typically include
 574      * the {@linkplain #getThreadName thread name},
 575      * the {@linkplain #getThreadId thread ID},
 576      * its {@linkplain #getThreadState state},
 577      * and a {@linkplain #getStackTrace stack trace} if any.
 578      *
 579      * @return a string representation of this thread info.
 580      */
 581     public String toString() {
 582         StringBuilder sb = new StringBuilder("\"" + getThreadName() + "\"" +
 583                                              " Id=" + getThreadId() + " " +
 584                                              getThreadState());
 585         if (getLockName() != null) {
 586             sb.append(" on " + getLockName());
 587         }
 588         if (getLockOwnerName() != null) {
 589             sb.append(" owned by \"" + getLockOwnerName() +
 590                       "\" Id=" + getLockOwnerId());
 591         }
 592         if (isSuspended()) {
 593             sb.append(" (suspended)");
 594         }
 595         if (isInNative()) {
 596             sb.append(" (in native)");
 597         }
 598         sb.append('\n');
 599         int i = 0;
 600         for (; i < stackTrace.length && i < MAX_FRAMES; i++) {
 601             StackTraceElement ste = stackTrace[i];
 602             sb.append("\tat " + ste.toString());
 603             sb.append('\n');
 604             if (i == 0 && getLockInfo() != null) {
 605                 Thread.State ts = getThreadState();
 606                 switch (ts) {
 607                     case BLOCKED:
 608                         sb.append("\t-  blocked on " + getLockInfo());
 609                         sb.append('\n');
 610                         break;
 611                     case WAITING:
 612                         sb.append("\t-  waiting on " + getLockInfo());
 613                         sb.append('\n');
 614                         break;
 615                     case TIMED_WAITING:
 616                         sb.append("\t-  waiting on " + getLockInfo());
 617                         sb.append('\n');
 618                         break;
 619                     default:
 620                 }
 621             }
 622 
 623             for (MonitorInfo mi : lockedMonitors) {
 624                 if (mi.getLockedStackDepth() == i) {
 625                     sb.append("\t-  locked " + mi);
 626                     sb.append('\n');
 627                 }
 628             }
 629        }
 630        if (i < stackTrace.length) {
 631            sb.append("\t...");
 632            sb.append('\n');
 633        }
 634 
 635        LockInfo[] locks = getLockedSynchronizers();
 636        if (locks.length > 0) {
 637            sb.append("\n\tNumber of locked synchronizers = " + locks.length);
 638            sb.append('\n');
 639            for (LockInfo li : locks) {
 640                sb.append("\t- " + li);
 641                sb.append('\n');
 642            }
 643        }
 644        sb.append('\n');
 645        return sb.toString();
 646     }
 647     private static final int MAX_FRAMES = 8;
 648 
 649     /**
 650      * Returns a <tt>ThreadInfo</tt> object represented by the
 651      * given <tt>CompositeData</tt>.
 652      * The given <tt>CompositeData</tt> must contain the following attributes
 653      * unless otherwise specified below:
 654      * <blockquote>
 655      * <table border>
 656      * <tr>
 657      *   <th align=left>Attribute Name</th>
 658      *   <th align=left>Type</th>
 659      * </tr>
 660      * <tr>
 661      *   <td>threadId</td>
 662      *   <td><tt>java.lang.Long</tt></td>
 663      * </tr>
 664      * <tr>
 665      *   <td>threadName</td>
 666      *   <td><tt>java.lang.String</tt></td>
 667      * </tr>
 668      * <tr>
 669      *   <td>threadState</td>
 670      *   <td><tt>java.lang.String</tt></td>
 671      * </tr>
 672      * <tr>
 673      *   <td>suspended</td>
 674      *   <td><tt>java.lang.Boolean</tt></td>
 675      * </tr>
 676      * <tr>
 677      *   <td>inNative</td>
 678      *   <td><tt>java.lang.Boolean</tt></td>
 679      * </tr>
 680      * <tr>
 681      *   <td>blockedCount</td>
 682      *   <td><tt>java.lang.Long</tt></td>
 683      * </tr>
 684      * <tr>
 685      *   <td>blockedTime</td>
 686      *   <td><tt>java.lang.Long</tt></td>
 687      * </tr>
 688      * <tr>
 689      *   <td>waitedCount</td>
 690      *   <td><tt>java.lang.Long</tt></td>
 691      * </tr>
 692      * <tr>
 693      *   <td>waitedTime</td>
 694      *   <td><tt>java.lang.Long</tt></td>
 695      * </tr>
 696      * <tr>
 697      *   <td>lockInfo</td>
 698      *   <td><tt>javax.management.openmbean.CompositeData</tt>
 699      *       - the mapped type for {@link LockInfo} as specified in the
 700      *         {@link LockInfo#from} method.
 701      *       <p>
 702      *       If <tt>cd</tt> does not contain this attribute,
 703      *       the <tt>LockInfo</tt> object will be constructed from
 704      *       the value of the <tt>lockName</tt> attribute. </td>
 705      * </tr>
 706      * <tr>
 707      *   <td>lockName</td>
 708      *   <td><tt>java.lang.String</tt></td>
 709      * </tr>
 710      * <tr>
 711      *   <td>lockOwnerId</td>
 712      *   <td><tt>java.lang.Long</tt></td>
 713      * </tr>
 714      * <tr>
 715      *   <td>lockOwnerName</td>
 716      *   <td><tt>java.lang.String</tt></td>
 717      * </tr>
 718      * <tr>
 719      *   <td><a name="StackTrace">stackTrace</a></td>
 720      *   <td><tt>javax.management.openmbean.CompositeData[]</tt>
 721      *       <p>
 722      *       Each element is a <tt>CompositeData</tt> representing
 723      *       StackTraceElement containing the following attributes:
 724      *       <blockquote>
 725      *       <table cellspacing=1 cellpadding=0>
 726      *       <tr>
 727      *         <th align=left>Attribute Name</th>
 728      *         <th align=left>Type</th>
 729      *       </tr>
 730      *       <tr>
 731      *         <td>className</td>
 732      *         <td><tt>java.lang.String</tt></td>
 733      *       </tr>
 734      *       <tr>
 735      *         <td>methodName</td>
 736      *         <td><tt>java.lang.String</tt></td>
 737      *       </tr>
 738      *       <tr>
 739      *         <td>fileName</td>
 740      *         <td><tt>java.lang.String</tt></td>
 741      *       </tr>
 742      *       <tr>
 743      *         <td>lineNumber</td>
 744      *         <td><tt>java.lang.Integer</tt></td>
 745      *       </tr>
 746      *       <tr>
 747      *         <td>nativeMethod</td>
 748      *         <td><tt>java.lang.Boolean</tt></td>
 749      *       </tr>
 750      *       </table>
 751      *       </blockquote>
 752      *   </td>
 753      * </tr>
 754      * <tr>
 755      *   <td>lockedMonitors</td>
 756      *   <td><tt>javax.management.openmbean.CompositeData[]</tt>
 757      *       whose element type is the mapped type for
 758      *       {@link MonitorInfo} as specified in the
 759      *       {@link MonitorInfo#from Monitor.from} method.
 760      *       <p>
 761      *       If <tt>cd</tt> does not contain this attribute,
 762      *       this attribute will be set to an empty array. </td>
 763      * </tr>
 764      * <tr>
 765      *   <td>lockedSynchronizers</td>
 766      *   <td><tt>javax.management.openmbean.CompositeData[]</tt>
 767      *       whose element type is the mapped type for
 768      *       {@link LockInfo} as specified in the {@link LockInfo#from} method.
 769      *       <p>
 770      *       If <tt>cd</tt> does not contain this attribute,
 771      *       this attribute will be set to an empty array. </td>
 772      * </tr>
 773      * </table>
 774      * </blockquote>
 775      *
 776      * @param cd <tt>CompositeData</tt> representing a <tt>ThreadInfo</tt>
 777      *
 778      * @throws IllegalArgumentException if <tt>cd</tt> does not
 779      *   represent a <tt>ThreadInfo</tt> with the attributes described
 780      *   above.
 781      *
 782      * @return a <tt>ThreadInfo</tt> object represented
 783      *         by <tt>cd</tt> if <tt>cd</tt> is not <tt>null</tt>;
 784      *         <tt>null</tt> otherwise.
 785      */
 786     public static ThreadInfo from(CompositeData cd) {
 787         if (cd == null) {
 788             return null;
 789         }
 790 
 791         if (cd instanceof ThreadInfoCompositeData) {
 792             return ((ThreadInfoCompositeData) cd).getThreadInfo();
 793         } else {
 794             return new ThreadInfo(cd);
 795         }
 796     }
 797 
 798     /**
 799      * Returns an array of {@link MonitorInfo} objects, each of which
 800      * represents an object monitor currently locked by the thread
 801      * associated with this <tt>ThreadInfo</tt>.
 802      * If no locked monitor was requested for this thread info or
 803      * no monitor is locked by the thread, this method
 804      * will return a zero-length array.
 805      *
 806      * @return an array of <tt>MonitorInfo</tt> objects representing
 807      *         the object monitors locked by the thread.
 808      *
 809      * @since 1.6
 810      */
 811     public MonitorInfo[] getLockedMonitors() {
 812         return lockedMonitors;
 813     }
 814 
 815     /**
 816      * Returns an array of {@link LockInfo} objects, each of which
 817      * represents an <a href="LockInfo.html#OwnableSynchronizer">ownable
 818      * synchronizer</a> currently locked by the thread associated with
 819      * this <tt>ThreadInfo</tt>.  If no locked synchronizer was
 820      * requested for this thread info or no synchronizer is locked by
 821      * the thread, this method will return a zero-length array.
 822      *
 823      * @return an array of <tt>LockInfo</tt> objects representing
 824      *         the ownable synchronizers locked by the thread.
 825      *
 826      * @since 1.6
 827      */
 828     public LockInfo[] getLockedSynchronizers() {
 829         return lockedSynchronizers;
 830     }
 831 
 832     private static final StackTraceElement[] NO_STACK_TRACE =
 833         new StackTraceElement[0];
 834 }