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