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 lockedMonitors List of locked monitors
 151      * @param lockedSynchronizers List of locked synchronizers
 152      */
 153     private ThreadInfo(Thread t, int state, Object lockObj, Thread lockOwner,
 154                        long blockedCount, long blockedTime,
 155                        long waitedCount, long waitedTime,
 156                        StackTraceElement[] stackTrace,
 157                        Object[] monitors,
 158                        int[] stackDepths,
 159                        Object[] synchronizers) {
 160         int numMonitors = (monitors == null ? 0 : monitors.length);
 161         MonitorInfo[] lockedMonitors;
 162         if (numMonitors == 0) {
 163             lockedMonitors = EMPTY_MONITORS;
 164         } else {
 165             lockedMonitors = new MonitorInfo[numMonitors];
 166             for (int i = 0; i < numMonitors; i++) {
 167                 Object lock = monitors[i];
 168                 String className = lock.getClass().getName();
 169                 int identityHashCode = System.identityHashCode(lock);
 170                 int depth = stackDepths[i];
 171                 StackTraceElement ste = (depth >= 0 ? stackTrace[depth]
 172                                                     : null);
 173                 lockedMonitors[i] = new MonitorInfo(className,
 174                                                     identityHashCode,
 175                                                     depth,
 176                                                     ste);
 177             }
 178         }
 179 
 180         int numSyncs = (synchronizers == null ? 0 : synchronizers.length);
 181         LockInfo[] lockedSynchronizers;
 182         if (numSyncs == 0) {
 183             lockedSynchronizers = EMPTY_SYNCS;
 184         } else {
 185             lockedSynchronizers = new LockInfo[numSyncs];
 186             for (int i = 0; i < numSyncs; i++) {
 187                 Object lock = synchronizers[i];
 188                 String className = lock.getClass().getName();
 189                 int identityHashCode = System.identityHashCode(lock);
 190                 lockedSynchronizers[i] = new LockInfo(className,
 191                                                       identityHashCode);
 192             }
 193         }
 194 
 195         initialize(t, state, lockObj, lockOwner,
 196                    blockedCount, blockedTime,
 197                    waitedCount, waitedTime, stackTrace,
 198                    lockedMonitors, lockedSynchronizers);
 199     }
 200 
 201     /**
 202      * Initialize ThreadInfo object
 203      *
 204      * @param t             Thread
 205      * @param state         Thread state
 206      * @param lockObj       Object on which the thread is blocked
 207      * @param lockOwner     the thread holding the lock
 208      * @param blockedCount  Number of times blocked to enter a lock
 209      * @param blockedTime   Approx time blocked to enter a lock
 210      * @param waitedCount   Number of times waited on a lock
 211      * @param waitedTime    Approx time waited on a lock
 212      * @param stackTrace    Thread stack trace
 213      * @param lockedMonitors List of locked monitors
 214      * @param lockedSynchronizers List of locked synchronizers
 215      */
 216     private void initialize(Thread t, int state, Object lockObj, Thread lockOwner,
 217                             long blockedCount, long blockedTime,
 218                             long waitedCount, long waitedTime,
 219                             StackTraceElement[] stackTrace,
 220                             MonitorInfo[] lockedMonitors,
 221                             LockInfo[] lockedSynchronizers) {
 222         this.threadId = t.getId();
 223         this.threadName = t.getName();
 224         this.threadState = ManagementFactoryHelper.toThreadState(state);
 225         this.suspended = ManagementFactoryHelper.isThreadSuspended(state);
 226         this.inNative = ManagementFactoryHelper.isThreadRunningNative(state);
 227         this.blockedCount = blockedCount;
 228         this.blockedTime = blockedTime;
 229         this.waitedCount = waitedCount;
 230         this.waitedTime = waitedTime;
 231 
 232         if (lockObj == null) {
 233             this.lock = null;
 234             this.lockName = null;
 235         } else {
 236             this.lock = new LockInfo(lockObj);
 237             this.lockName =
 238                 lock.getClassName() + '@' +
 239                     Integer.toHexString(lock.getIdentityHashCode());
 240         }
 241         if (lockOwner == null) {
 242             this.lockOwnerId = -1;
 243             this.lockOwnerName = null;
 244         } else {
 245             this.lockOwnerId = lockOwner.getId();
 246             this.lockOwnerName = lockOwner.getName();
 247         }
 248         if (stackTrace == null) {
 249             this.stackTrace = NO_STACK_TRACE;
 250         } else {
 251             this.stackTrace = stackTrace;
 252         }
 253         this.lockedMonitors = lockedMonitors;
 254         this.lockedSynchronizers = lockedSynchronizers;
 255     }
 256 
 257     /*
 258      * Constructs a <tt>ThreadInfo</tt> object from a
 259      * {@link CompositeData CompositeData}.
 260      */
 261     private ThreadInfo(CompositeData cd) {
 262         ThreadInfoCompositeData ticd = ThreadInfoCompositeData.getInstance(cd);
 263 
 264         threadId = ticd.threadId();
 265         threadName = ticd.threadName();
 266         blockedTime = ticd.blockedTime();
 267         blockedCount = ticd.blockedCount();
 268         waitedTime = ticd.waitedTime();
 269         waitedCount = ticd.waitedCount();
 270         lockName = ticd.lockName();
 271         lockOwnerId = ticd.lockOwnerId();
 272         lockOwnerName = ticd.lockOwnerName();
 273         threadState = ticd.threadState();
 274         suspended = ticd.suspended();
 275         inNative = ticd.inNative();
 276         stackTrace = ticd.stackTrace();
 277 
 278         // 6.0 attributes
 279         if (ticd.isCurrentVersion()) {
 280             lock = ticd.lockInfo();
 281             lockedMonitors = ticd.lockedMonitors();
 282             lockedSynchronizers = ticd.lockedSynchronizers();
 283         } else {
 284             // lockInfo is a new attribute added in 1.6 ThreadInfo
 285             // If cd is a 5.0 version, construct the LockInfo object
 286             //  from the lockName value.
 287             if (lockName != null) {
 288                 String result[] = lockName.split("@");
 289                 if (result.length == 2) {
 290                     int identityHashCode = Integer.parseInt(result[1], 16);
 291                     lock = new LockInfo(result[0], identityHashCode);
 292                 } else {
 293                     assert result.length == 2;
 294                     lock = null;
 295                 }
 296             } else {
 297                 lock = null;
 298             }
 299             lockedMonitors = EMPTY_MONITORS;
 300             lockedSynchronizers = EMPTY_SYNCS;
 301         }
 302     }
 303 
 304     /**
 305      * Returns the ID of the thread associated with this <tt>ThreadInfo</tt>.
 306      *
 307      * @return the ID of the associated thread.
 308      */
 309     public long getThreadId() {
 310         return threadId;
 311     }
 312 
 313     /**
 314      * Returns the name of the thread associated with this <tt>ThreadInfo</tt>.
 315      *
 316      * @return the name of the associated thread.
 317      */
 318     public String getThreadName() {
 319         return threadName;
 320     }
 321 
 322     /**
 323      * Returns the state of the thread associated with this <tt>ThreadInfo</tt>.
 324      *
 325      * @return <tt>Thread.State</tt> of the associated thread.
 326      */
 327     public Thread.State getThreadState() {
 328          return threadState;
 329     }
 330 
 331     /**
 332      * Returns the approximate accumulated elapsed time (in milliseconds)
 333      * that the thread associated with this <tt>ThreadInfo</tt>
 334      * has blocked to enter or reenter a monitor
 335      * since thread contention monitoring is enabled.
 336      * I.e. the total accumulated time the thread has been in the
 337      * {@link java.lang.Thread.State#BLOCKED BLOCKED} state since thread
 338      * contention monitoring was last enabled.
 339      * This method returns <tt>-1</tt> if thread contention monitoring
 340      * is disabled.
 341      *
 342      * <p>The Java virtual machine may measure the time with a high
 343      * resolution timer.  This statistic is reset when
 344      * the thread contention monitoring is reenabled.
 345      *
 346      * @return the approximate accumulated elapsed time in milliseconds
 347      * that a thread entered the <tt>BLOCKED</tt> state;
 348      * <tt>-1</tt> if thread contention monitoring is disabled.
 349      *
 350      * @throws java.lang.UnsupportedOperationException if the Java
 351      * virtual machine does not support this operation.
 352      *
 353      * @see ThreadMXBean#isThreadContentionMonitoringSupported
 354      * @see ThreadMXBean#setThreadContentionMonitoringEnabled
 355      */
 356     public long getBlockedTime() {
 357         return blockedTime;
 358     }
 359 
 360     /**
 361      * Returns the total number of times that
 362      * the thread associated with this <tt>ThreadInfo</tt>
 363      * blocked to enter or reenter a monitor.
 364      * I.e. the number of times a thread has been in the
 365      * {@link java.lang.Thread.State#BLOCKED BLOCKED} state.
 366      *
 367      * @return the total number of times that the thread
 368      * entered the <tt>BLOCKED</tt> state.
 369      */
 370     public long getBlockedCount() {
 371         return blockedCount;
 372     }
 373 
 374     /**
 375      * Returns the approximate accumulated elapsed time (in milliseconds)
 376      * that the thread associated with this <tt>ThreadInfo</tt>
 377      * has waited for notification
 378      * since thread contention monitoring is enabled.
 379      * I.e. the total accumulated time the thread has been in the
 380      * {@link java.lang.Thread.State#WAITING WAITING}
 381      * or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state
 382      * since thread contention monitoring is enabled.
 383      * This method returns <tt>-1</tt> if thread contention monitoring
 384      * is disabled.
 385      *
 386      * <p>The Java virtual machine may measure the time with a high
 387      * resolution timer.  This statistic is reset when
 388      * the thread contention monitoring is reenabled.
 389      *
 390      * @return the approximate accumulated elapsed time in milliseconds
 391      * that a thread has been in the <tt>WAITING</tt> or
 392      * <tt>TIMED_WAITING</tt> state;
 393      * <tt>-1</tt> if thread contention monitoring is disabled.
 394      *
 395      * @throws java.lang.UnsupportedOperationException if the Java
 396      * virtual machine does not support this operation.
 397      *
 398      * @see ThreadMXBean#isThreadContentionMonitoringSupported
 399      * @see ThreadMXBean#setThreadContentionMonitoringEnabled
 400      */
 401     public long getWaitedTime() {
 402         return waitedTime;
 403     }
 404 
 405     /**
 406      * Returns the total number of times that
 407      * the thread associated with this <tt>ThreadInfo</tt>
 408      * waited for notification.
 409      * I.e. the number of times that a thread has been
 410      * in the {@link java.lang.Thread.State#WAITING WAITING}
 411      * or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state.
 412      *
 413      * @return the total number of times that the thread
 414      * was in the <tt>WAITING</tt> or <tt>TIMED_WAITING</tt> state.
 415      */
 416     public long getWaitedCount() {
 417         return waitedCount;
 418     }
 419 
 420     /**
 421      * Returns the <tt>LockInfo</tt> of an object for which
 422      * the thread associated with this <tt>ThreadInfo</tt>
 423      * is blocked waiting.
 424      * A thread can be blocked waiting for one of the following:
 425      * <ul>
 426      * <li>an object monitor to be acquired for entering or reentering
 427      *     a synchronization block/method.
 428      *     <br>The thread is in the {@link java.lang.Thread.State#BLOCKED BLOCKED}
 429      *     state waiting to enter the <tt>synchronized</tt> statement
 430      *     or method.
 431      *     <p></li>
 432      * <li>an object monitor to be notified by another thread.
 433      *     <br>The thread is in the {@link java.lang.Thread.State#WAITING WAITING}
 434      *     or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state
 435      *     due to a call to the {@link Object#wait Object.wait} method.
 436      *     <p></li>
 437      * <li>a synchronization object responsible for the thread parking.
 438      *     <br>The thread is in the {@link java.lang.Thread.State#WAITING WAITING}
 439      *     or {@link java.lang.Thread.State#TIMED_WAITING TIMED_WAITING} state
 440      *     due to a call to the
 441      *     {@link java.util.concurrent.locks.LockSupport#park(Object)
 442      *     LockSupport.park} method.  The synchronization object
 443      *     is the object returned from
 444      *     {@link java.util.concurrent.locks.LockSupport#getBlocker
 445      *     LockSupport.getBlocker} method. Typically it is an
 446      *     <a href="LockInfo.html#OwnableSynchronizer"> ownable synchronizer</a>
 447      *     or a {@link java.util.concurrent.locks.Condition Condition}.</li>
 448      * </ul>
 449      *
 450      * <p>This method returns <tt>null</tt> if the thread is not in any of
 451      * the above conditions.
 452      *
 453      * @return <tt>LockInfo</tt> of an object for which the thread
 454      *         is blocked waiting if any; <tt>null</tt> otherwise.
 455      * @since 1.6
 456      */
 457     public LockInfo getLockInfo() {
 458         return lock;
 459     }
 460 
 461     /**
 462      * Returns the {@link LockInfo#toString string representation}
 463      * of an object for which the thread associated with this
 464      * <tt>ThreadInfo</tt> is blocked waiting.
 465      * This method is equivalent to calling:
 466      * <blockquote>
 467      * <pre>
 468      * getLockInfo().toString()
 469      * </pre></blockquote>
 470      *
 471      * <p>This method will return <tt>null</tt> if this thread is not blocked
 472      * waiting for any object or if the object is not owned by any thread.
 473      *
 474      * @return the string representation of the object on which
 475      * the thread is blocked if any;
 476      * <tt>null</tt> otherwise.
 477      *
 478      * @see #getLockInfo
 479      */
 480     public String getLockName() {
 481         return lockName;
 482     }
 483 
 484     /**
 485      * Returns the ID of the thread which owns the object
 486      * for which the thread associated with this <tt>ThreadInfo</tt>
 487      * is blocked waiting.
 488      * This method will return <tt>-1</tt> if this thread is not blocked
 489      * waiting for any object or if the object is not owned by any thread.
 490      *
 491      * @return the thread ID of the owner thread of the object
 492      * this thread is blocked on;
 493      * <tt>-1</tt> if this thread is not blocked
 494      * or if the object lis not owned by any thread.
 495      *
 496      * @see #getLockInfo
 497      */
 498     public long getLockOwnerId() {
 499         return lockOwnerId;
 500     }
 501 
 502     /**
 503      * Returns the name of the thread which owns the object
 504      * for which the thread associated with this <tt>ThreadInfo</tt>
 505      * is blocked waiting.
 506      * This method will return <tt>null</tt> if this thread is not blocked
 507      * waiting for any object or if the object is not owned by any thread.
 508      *
 509      * @return the name of the thread that owns the object
 510      * this thread is blocked on;
 511      * <tt>null</tt> if this thread is not blocked
 512      * or if the object is not owned by any thread.
 513      *
 514      * @see #getLockInfo
 515      */
 516     public String getLockOwnerName() {
 517         return lockOwnerName;
 518     }
 519 
 520     /**
 521      * Returns the stack trace of the thread
 522      * associated with this <tt>ThreadInfo</tt>.
 523      * If no stack trace was requested for this thread info, this method
 524      * will return a zero-length array.
 525      * If the returned array is of non-zero length then the first element of
 526      * the array represents the top of the stack, which is the most recent
 527      * method invocation in the sequence.  The last element of the array
 528      * represents the bottom of the stack, which is the least recent method
 529      * invocation in the sequence.
 530      *
 531      * <p>Some Java virtual machines may, under some circumstances, omit one
 532      * or more stack frames from the stack trace.  In the extreme case,
 533      * a virtual machine that has no stack trace information concerning
 534      * the thread associated with this <tt>ThreadInfo</tt>
 535      * is permitted to return a zero-length array from this method.
 536      *
 537      * @return an array of <tt>StackTraceElement</tt> objects of the thread.
 538      */
 539     public StackTraceElement[] getStackTrace() {
 540         return stackTrace;
 541     }
 542 
 543     /**
 544      * Tests if the thread associated with this <tt>ThreadInfo</tt>
 545      * is suspended.  This method returns <tt>true</tt> if
 546      * {@link Thread#suspend} has been called.
 547      *
 548      * @return <tt>true</tt> if the thread is suspended;
 549      *         <tt>false</tt> otherwise.
 550      */
 551     public boolean isSuspended() {
 552          return suspended;
 553     }
 554 
 555     /**
 556      * Tests if the thread associated with this <tt>ThreadInfo</tt>
 557      * is executing native code via the Java Native Interface (JNI).
 558      * The JNI native code does not include
 559      * the virtual machine support code or the compiled native
 560      * code generated by the virtual machine.
 561      *
 562      * @return <tt>true</tt> if the thread is executing native code;
 563      *         <tt>false</tt> otherwise.
 564      */
 565     public boolean isInNative() {
 566          return inNative;
 567     }
 568 
 569     /**
 570      * Returns a string representation of this thread info.
 571      * The format of this string depends on the implementation.
 572      * The returned string will typically include
 573      * the {@linkplain #getThreadName thread name},
 574      * the {@linkplain #getThreadId thread ID},
 575      * its {@linkplain #getThreadState state},
 576      * and a {@linkplain #getStackTrace stack trace} if any.
 577      *
 578      * @return a string representation of this thread info.
 579      */
 580     public String toString() {
 581         StringBuilder sb = new StringBuilder("\"" + getThreadName() + "\"" +
 582                                              " Id=" + getThreadId() + " " +
 583                                              getThreadState());
 584         if (getLockName() != null) {
 585             sb.append(" on " + getLockName());
 586         }
 587         if (getLockOwnerName() != null) {
 588             sb.append(" owned by \"" + getLockOwnerName() +
 589                       "\" Id=" + getLockOwnerId());
 590         }
 591         if (isSuspended()) {
 592             sb.append(" (suspended)");
 593         }
 594         if (isInNative()) {
 595             sb.append(" (in native)");
 596         }
 597         sb.append('\n');
 598         int i = 0;
 599         for (; i < stackTrace.length && i < MAX_FRAMES; i++) {
 600             StackTraceElement ste = stackTrace[i];
 601             sb.append("\tat " + ste.toString());
 602             sb.append('\n');
 603             if (i == 0 && getLockInfo() != null) {
 604                 Thread.State ts = getThreadState();
 605                 switch (ts) {
 606                     case BLOCKED:
 607                         sb.append("\t-  blocked on " + getLockInfo());
 608                         sb.append('\n');
 609                         break;
 610                     case WAITING:
 611                         sb.append("\t-  waiting on " + getLockInfo());
 612                         sb.append('\n');
 613                         break;
 614                     case TIMED_WAITING:
 615                         sb.append("\t-  waiting on " + getLockInfo());
 616                         sb.append('\n');
 617                         break;
 618                     default:
 619                 }
 620             }
 621 
 622             for (MonitorInfo mi : lockedMonitors) {
 623                 if (mi.getLockedStackDepth() == i) {
 624                     sb.append("\t-  locked " + mi);
 625                     sb.append('\n');
 626                 }
 627             }
 628        }
 629        if (i < stackTrace.length) {
 630            sb.append("\t...");
 631            sb.append('\n');
 632        }
 633 
 634        LockInfo[] locks = getLockedSynchronizers();
 635        if (locks.length > 0) {
 636            sb.append("\n\tNumber of locked synchronizers = " + locks.length);
 637            sb.append('\n');
 638            for (LockInfo li : locks) {
 639                sb.append("\t- " + li);
 640                sb.append('\n');
 641            }
 642        }
 643        sb.append('\n');
 644        return sb.toString();
 645     }
 646     private static final int MAX_FRAMES = 8;
 647 
 648     /**
 649      * Returns a <tt>ThreadInfo</tt> object represented by the
 650      * given <tt>CompositeData</tt>.
 651      * The given <tt>CompositeData</tt> must contain the following attributes
 652      * unless otherwise specified below:
 653      * <blockquote>
 654      * <table border>
 655      * <tr>
 656      *   <th align=left>Attribute Name</th>
 657      *   <th align=left>Type</th>
 658      * </tr>
 659      * <tr>
 660      *   <td>threadId</td>
 661      *   <td><tt>java.lang.Long</tt></td>
 662      * </tr>
 663      * <tr>
 664      *   <td>threadName</td>
 665      *   <td><tt>java.lang.String</tt></td>
 666      * </tr>
 667      * <tr>
 668      *   <td>threadState</td>
 669      *   <td><tt>java.lang.String</tt></td>
 670      * </tr>
 671      * <tr>
 672      *   <td>suspended</td>
 673      *   <td><tt>java.lang.Boolean</tt></td>
 674      * </tr>
 675      * <tr>
 676      *   <td>inNative</td>
 677      *   <td><tt>java.lang.Boolean</tt></td>
 678      * </tr>
 679      * <tr>
 680      *   <td>blockedCount</td>
 681      *   <td><tt>java.lang.Long</tt></td>
 682      * </tr>
 683      * <tr>
 684      *   <td>blockedTime</td>
 685      *   <td><tt>java.lang.Long</tt></td>
 686      * </tr>
 687      * <tr>
 688      *   <td>waitedCount</td>
 689      *   <td><tt>java.lang.Long</tt></td>
 690      * </tr>
 691      * <tr>
 692      *   <td>waitedTime</td>
 693      *   <td><tt>java.lang.Long</tt></td>
 694      * </tr>
 695      * <tr>
 696      *   <td>lockInfo</td>
 697      *   <td><tt>javax.management.openmbean.CompositeData</tt>
 698      *       - the mapped type for {@link LockInfo} as specified in the
 699      *         {@link LockInfo#from} method.
 700      *       <p>
 701      *       If <tt>cd</tt> does not contain this attribute,
 702      *       the <tt>LockInfo</tt> object will be constructed from
 703      *       the value of the <tt>lockName</tt> attribute. </td>
 704      * </tr>
 705      * <tr>
 706      *   <td>lockName</td>
 707      *   <td><tt>java.lang.String</tt></td>
 708      * </tr>
 709      * <tr>
 710      *   <td>lockOwnerId</td>
 711      *   <td><tt>java.lang.Long</tt></td>
 712      * </tr>
 713      * <tr>
 714      *   <td>lockOwnerName</td>
 715      *   <td><tt>java.lang.String</tt></td>
 716      * </tr>
 717      * <tr>
 718      *   <td><a name="StackTrace">stackTrace</a></td>
 719      *   <td><tt>javax.management.openmbean.CompositeData[]</tt>
 720      *       <p>
 721      *       Each element is a <tt>CompositeData</tt> representing
 722      *       StackTraceElement containing the following attributes:
 723      *       <blockquote>
 724      *       <table cellspacing=1 cellpadding=0>
 725      *       <tr>
 726      *         <th align=left>Attribute Name</th>
 727      *         <th align=left>Type</th>
 728      *       </tr>
 729      *       <tr>
 730      *         <td>className</td>
 731      *         <td><tt>java.lang.String</tt></td>
 732      *       </tr>
 733      *       <tr>
 734      *         <td>methodName</td>
 735      *         <td><tt>java.lang.String</tt></td>
 736      *       </tr>
 737      *       <tr>
 738      *         <td>fileName</td>
 739      *         <td><tt>java.lang.String</tt></td>
 740      *       </tr>
 741      *       <tr>
 742      *         <td>lineNumber</td>
 743      *         <td><tt>java.lang.Integer</tt></td>
 744      *       </tr>
 745      *       <tr>
 746      *         <td>nativeMethod</td>
 747      *         <td><tt>java.lang.Boolean</tt></td>
 748      *       </tr>
 749      *       </table>
 750      *       </blockquote>
 751      *   </td>
 752      * </tr>
 753      * <tr>
 754      *   <td>lockedMonitors</td>
 755      *   <td><tt>javax.management.openmbean.CompositeData[]</tt>
 756      *       whose element type is the mapped type for
 757      *       {@link MonitorInfo} as specified in the
 758      *       {@link MonitorInfo#from Monitor.from} method.
 759      *       <p>
 760      *       If <tt>cd</tt> does not contain this attribute,
 761      *       this attribute will be set to an empty array. </td>
 762      * </tr>
 763      * <tr>
 764      *   <td>lockedSynchronizers</td>
 765      *   <td><tt>javax.management.openmbean.CompositeData[]</tt>
 766      *       whose element type is the mapped type for
 767      *       {@link LockInfo} as specified in the {@link LockInfo#from} method.
 768      *       <p>
 769      *       If <tt>cd</tt> does not contain this attribute,
 770      *       this attribute will be set to an empty array. </td>
 771      * </tr>
 772      * </table>
 773      * </blockquote>
 774      *
 775      * @param cd <tt>CompositeData</tt> representing a <tt>ThreadInfo</tt>
 776      *
 777      * @throws IllegalArgumentException if <tt>cd</tt> does not
 778      *   represent a <tt>ThreadInfo</tt> with the attributes described
 779      *   above.
 780      *
 781      * @return a <tt>ThreadInfo</tt> object represented
 782      *         by <tt>cd</tt> if <tt>cd</tt> is not <tt>null</tt>;
 783      *         <tt>null</tt> otherwise.
 784      */
 785     public static ThreadInfo from(CompositeData cd) {
 786         if (cd == null) {
 787             return null;
 788         }
 789 
 790         if (cd instanceof ThreadInfoCompositeData) {
 791             return ((ThreadInfoCompositeData) cd).getThreadInfo();
 792         } else {
 793             return new ThreadInfo(cd);
 794         }
 795     }
 796 
 797     /**
 798      * Returns an array of {@link MonitorInfo} objects, each of which
 799      * represents an object monitor currently locked by the thread
 800      * associated with this <tt>ThreadInfo</tt>.
 801      * If no locked monitor was requested for this thread info or
 802      * no monitor is locked by the thread, this method
 803      * will return a zero-length array.
 804      *
 805      * @return an array of <tt>MonitorInfo</tt> objects representing
 806      *         the object monitors locked by the thread.
 807      *
 808      * @since 1.6
 809      */
 810     public MonitorInfo[] getLockedMonitors() {
 811         return lockedMonitors;
 812     }
 813 
 814     /**
 815      * Returns an array of {@link LockInfo} objects, each of which
 816      * represents an <a href="LockInfo.html#OwnableSynchronizer">ownable
 817      * synchronizer</a> currently locked by the thread associated with
 818      * this <tt>ThreadInfo</tt>.  If no locked synchronizer was
 819      * requested for this thread info or no synchronizer is locked by
 820      * the thread, this method will return a zero-length array.
 821      *
 822      * @return an array of <tt>LockInfo</tt> objects representing
 823      *         the ownable synchronizers locked by the thread.
 824      *
 825      * @since 1.6
 826      */
 827     public LockInfo[] getLockedSynchronizers() {
 828         return lockedSynchronizers;
 829     }
 830 
 831     private static final StackTraceElement[] NO_STACK_TRACE =
 832         new StackTraceElement[0];
 833 }