001/* ThreadInfo.java - Information on a thread
002   Copyright (C) 2006 Free Software Foundation
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package java.lang.management;
039
040import java.util.Arrays;
041
042import javax.management.openmbean.ArrayType;
043import javax.management.openmbean.CompositeData;
044import javax.management.openmbean.CompositeType;
045import javax.management.openmbean.OpenDataException;
046import javax.management.openmbean.OpenType;
047import javax.management.openmbean.SimpleType;
048
049/**
050 * <p>
051 * A class which maintains information about a particular
052 * thread.  This information includes:
053 * </p>
054 * <ul>
055 * <li><strong>General Thread Information:</strong>
056 * <ul>
057 * <li>The identifier of the thread.</li>
058 * <li>The name of the thread.</li>
059 * </ul>
060 * </li>
061 * <li><strong>Execution Information:</strong>
062 * <ul>
063 * <li>The current state of the thread (e.g. blocked, runnable)</li>
064 * <li>The object upon which the thread is blocked, either because
065 * the thread is waiting to obtain the monitor of that object to enter
066 * one of its synchronized monitor, or because
067 * {@link java.lang.Object#wait()} has been called while the thread
068 * was within a method of that object.</li>
069 * <li>The thread identifier of the current thread holding an object's
070 * monitor, upon which the thread described here is blocked.</li>
071 * <li>The stack trace of the thread (if requested on creation
072 * of this object</li>
073 * <li>The current locks held on object monitors by the thread.</li>
074 * <li>The current locks held on ownable synchronizers by the thread.</li>
075 * </ul>
076 * <li><strong>Synchronization Statistics</strong>
077 * <ul>
078 * <li>The number of times the thread has been blocked waiting for
079 * an object's monitor or in a {@link java.lang.Object#wait()} call.</li>
080 * <li>The accumulated time the thread has been blocked waiting for
081 * an object's monitor on in a {@link java.lang.Object#wait()} call.
082 * The availability of these statistics depends on the virtual machine's
083 * support for thread contention monitoring (see
084 * {@link ThreadMXBean#isThreadContentionMonitoringSupported()}.</li>
085 * </ul>
086 * </li>
087 * </ul>
088 *
089 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
090 * @since 1.5
091 * @see ThreadMXBean#isThreadContentionMonitoringSupported()
092 */
093public class ThreadInfo
094{
095
096  /**
097   * The id of the thread which this instance concerns.
098   */
099  private long threadId;
100
101  /**
102   * The name of the thread which this instance concerns.
103   */
104  private String threadName;
105
106  /**
107   * The state of the thread which this instance concerns.
108   */
109  private Thread.State threadState;
110
111  /**
112   * The number of times the thread has been blocked.
113   */
114  private long blockedCount;
115
116  /**
117   * The accumulated number of milliseconds the thread has
118   * been blocked (used only with thread contention monitoring
119   * support).
120   */
121  private long blockedTime;
122
123  /**
124   * The name of the monitor lock on which this thread
125   * is blocked (if any).
126   */
127  private String lockName;
128
129  /**
130   * The id of the thread which owns the monitor lock on
131   * which this thread is blocked, or <code>-1</code>
132   * if there is no owner.
133   */
134  private long lockOwnerId;
135
136  /**
137   * The name of the thread which owns the monitor lock on
138   * which this thread is blocked, or <code>null</code>
139   * if there is no owner.
140   */
141  private String lockOwnerName;
142
143  /**
144   * The number of times the thread has been in a waiting
145   * state.
146   */
147  private long waitedCount;
148
149  /**
150   * The accumulated number of milliseconds the thread has
151   * been waiting (used only with thread contention monitoring
152   * support).
153   */
154  private long waitedTime;
155
156  /**
157   * True if the thread is in a native method.
158   */
159  private boolean isInNative;
160
161  /**
162   * True if the thread is suspended.
163   */
164  private boolean isSuspended;
165
166  /**
167   * The stack trace of the thread.
168   */
169  private StackTraceElement[] trace;
170
171  /**
172   * The array of information on monitors locked by the thread.
173   */
174  private MonitorInfo[] lockedMonitors;
175
176  /**
177   * The array of information on ownable synchronizers locked
178   * by the thread.
179   */
180  private LockInfo[] lockedSynchronizers;
181
182  /**
183   * Cache a local reference to the thread management bean.
184   */
185  private static ThreadMXBean bean = null;
186
187  /**
188   * Cache the {@link javax.management.openmbean.CompositeType}
189   * for the {@link StackTraceElement}.
190   */
191  private static CompositeType seType;
192
193  /**
194   * Constructs a new {@link ThreadInfo} corresponding
195   * to the thread details specified.
196   *
197   * @param threadId the id of the thread on which this
198   *                 new instance will be based.
199   * @param threadName the name of the thread on which
200   *                 this new instance will be based.
201   * @param threadState the state of the thread on which
202   *                 this new instance will be based.
203   * @param blockedCount the number of times the thread
204   *                     has been blocked.
205   * @param blockedTime the accumulated number of milliseconds
206   *                    the specified thread has been blocked
207   *                    (only used with contention monitoring enabled)
208   * @param lockName the name of the monitor lock the thread is waiting for
209   *                 (only used if blocked)
210   * @param lockOwnerId the id of the thread which owns the monitor
211   *                  lock, or <code>-1</code> if it doesn't have an owner
212   *                  (only used if blocked)
213   * @param lockOwnerName the name of the thread which owns the monitor
214   *                  lock, or <code>null</code> if it doesn't have an 
215   *                  owner (only used if blocked)
216   * @param waitedCount the number of times the thread has been in a
217   *                    waiting state.
218   * @param waitedTime the accumulated number of milliseconds the
219   *                   specified thread has been waiting
220   *                   (only used with contention monitoring enabled)
221   * @param isInNative true if the thread is in a native method.
222   * @param isSuspended true if the thread is suspended.
223   * @param trace the stack trace of the thread to a pre-determined
224   *              depth (see VMThreadMXBeanImpl)
225   * @param lockedMonitors an array of {@link MonitorInfo} objects
226   *                       representing locks held on object monitors
227   *                       by the thread.
228   * @param lockedSynchronizers an array of {@link LockInfo} objects
229   *                            representing locks held on ownable
230   *                            synchronizers by the thread. 
231   *
232   * @since 1.6
233   */
234  private ThreadInfo(long threadId, String threadName, Thread.State threadState,
235                     long blockedCount, long blockedTime, String lockName, 
236                     long lockOwnerId, String lockOwnerName, long waitedCount,
237                     long waitedTime, boolean isInNative, boolean isSuspended,
238                     StackTraceElement[] trace, MonitorInfo[] lockedMonitors,
239                     LockInfo[] lockedSynchronizers)
240  {
241    this.threadId = threadId;
242    this.threadName = threadName;
243    this.threadState = threadState;
244    this.blockedCount = blockedCount;
245    this.blockedTime = blockedTime;
246    this.lockName = lockName;
247    this.lockOwnerId = lockOwnerId;
248    this.lockOwnerName = lockOwnerName;
249    this.waitedCount = waitedCount;
250    this.waitedTime = waitedTime;
251    this.isInNative = isInNative;
252    this.isSuspended = isSuspended;
253    this.trace = trace;
254    this.lockedMonitors = lockedMonitors;
255    this.lockedSynchronizers = lockedSynchronizers;
256  }
257
258  /**
259   * Checks for an attribute in a {@link CompositeData} structure
260   * with the correct type.
261   *
262   * @param ctype the composite data type to check.
263   * @param name the name of the attribute.
264   * @param type the type to check for.
265   * @throws IllegalArgumentException if the attribute is absent
266   *                                  or of the wrong type.
267   */
268  static void checkAttribute(CompositeType ctype, String name,
269                             OpenType type)
270    throws IllegalArgumentException
271  {
272    OpenType foundType = ctype.getType(name);
273    if (foundType == null)
274      throw new IllegalArgumentException("Could not find a field named " +
275                                         name);
276    if (!(foundType.equals(type)))
277      throw new IllegalArgumentException("Field " + name + " is not of " +
278                                         "type " + type.getClassName());
279  }
280
281  /**
282   * Returns the {@link javax.management.openmbean.CompositeType} for
283   * a {@link StackTraceElement}.
284   *
285   * @return the type for the stack trace element.
286   */
287  static CompositeType getStackTraceType()
288  {
289    if (seType == null)
290      try
291        {
292          seType = new CompositeType(StackTraceElement.class.getName(),
293                                     "An element of a stack trace",
294                                     new String[] { "className", "methodName",
295                                                    "fileName", "lineNumber",
296                                                    "nativeMethod" 
297                                     },
298                                     new String[] { "Name of the class",
299                                                    "Name of the method",
300                                                    "Name of the source code file",
301                                                    "Line number",
302                                                    "True if this is a native method" 
303                                     },
304                                     new OpenType[] {
305                                       SimpleType.STRING, SimpleType.STRING,
306                                       SimpleType.STRING, SimpleType.INTEGER,
307                                       SimpleType.BOOLEAN 
308                                     });
309        }
310      catch (OpenDataException e)
311        {
312          throw new IllegalStateException("Something went wrong in creating " +
313                                          "the composite data type for the " +
314                                          "stack trace element.", e);
315        }
316    return seType;
317  }
318
319  /**
320   * <p>
321   * Returns a {@link ThreadInfo} instance using the values
322   * given in the supplied
323   * {@link javax.management.openmbean.CompositeData} object.
324   * The composite data instance should contain the following
325   * attributes with the specified types:
326   * </p>
327   * <table>
328   * <th><td>Name</td><td>Type</td></th>
329   * <tr><td>threadId</td><td>java.lang.Long</td></tr>
330   * <tr><td>threadName</td><td>java.lang.String</td></tr>
331   * <tr><td>threadState</td><td>java.lang.String</td></tr>
332   * <tr><td>suspended</td><td>java.lang.Boolean</td></tr>
333   * <tr><td>inNative</td><td>java.lang.Boolean</td></tr>
334   * <tr><td>blockedCount</td><td>java.lang.Long</td></tr>
335   * <tr><td>blockedTime</td><td>java.lang.Long</td></tr>
336   * <tr><td>waitedCount</td><td>java.lang.Long</td></tr>
337   * <tr><td>waitedTime</td><td>java.lang.Long</td></tr>
338   * <tr><td>lockName</td><td>java.lang.String</td></tr>
339   * <tr><td>lockOwnerId</td><td>java.lang.Long</td></tr>
340   * <tr><td>lockOwnerName</td><td>java.lang.String</td></tr>
341   * <tr><td>stackTrace</td><td>javax.management.openmbean.CompositeData[]
342   * </td></tr>
343   * </table>
344   * <p>
345   * The stack trace is further described as:
346   * </p>
347   * <table>
348   * <th><td>Name</td><td>Type</td></th>
349   * <tr><td>className</td><td>java.lang.String</td></tr>
350   * <tr><td>methodName</td><td>java.lang.String</td></tr>
351   * <tr><td>fileName</td><td>java.lang.String</td></tr>
352   * <tr><td>lineNumber</td><td>java.lang.Integer</td></tr>
353   * <tr><td>nativeMethod</td><td>java.lang.Boolean</td></tr>
354   * </table>
355   * 
356   * @param data the composite data structure to take values from.
357   * @return a new instance containing the values from the 
358   *         composite data structure, or <code>null</code>
359   *         if the data structure was also <code>null</code>.
360   * @throws IllegalArgumentException if the composite data structure
361   *                                  does not match the structure
362   *                                  outlined above.
363   */
364  public static ThreadInfo from(CompositeData data)
365  {
366    if (data == null)
367      return null;
368    CompositeType type = data.getCompositeType();
369    checkAttribute(type, "ThreadId", SimpleType.LONG);
370    checkAttribute(type, "ThreadName", SimpleType.STRING);
371    checkAttribute(type, "ThreadState", SimpleType.STRING);
372    checkAttribute(type, "Suspended", SimpleType.BOOLEAN);
373    checkAttribute(type, "InNative", SimpleType.BOOLEAN);
374    checkAttribute(type, "BlockedCount", SimpleType.LONG);
375    checkAttribute(type, "BlockedTime", SimpleType.LONG);
376    checkAttribute(type, "WaitedCount", SimpleType.LONG);
377    checkAttribute(type, "WaitedTime", SimpleType.LONG);
378    checkAttribute(type, "LockName", SimpleType.STRING);
379    checkAttribute(type, "LockOwnerId", SimpleType.LONG);
380    checkAttribute(type, "LockOwnerName", SimpleType.STRING);
381    try
382      {
383        checkAttribute(type, "StackTrace",
384                       new ArrayType(1, getStackTraceType()));
385      }
386    catch (OpenDataException e)
387      {
388        throw new IllegalStateException("Something went wrong in creating " +
389                                        "the array for the stack trace element.",
390                                        e);
391      }
392    OpenType foundType = type.getType("LockedMonitors");
393    if (foundType != null)
394      try
395        {
396          CompositeType mType = new CompositeType(MonitorInfo.class.getName(),
397                                                  "Information on a object monitor lock",
398                                                  new String[] { "ClassName",
399                                                                 "IdentityHashCode",
400                                                                 "LockedStackDepth",
401                                                                 "LockedStackFrame"
402                                                  },
403                                                  new String[] { "Name of the class",
404                                                                 "Identity hash code " +
405                                                                 "of the class",
406                                                                 "Stack depth at time " +
407                                                                 "of lock",
408                                                                 "Stack frame at time " +
409                                                                 "of lock",
410                                                  },
411                                                  new OpenType[] {
412                                                    SimpleType.STRING, SimpleType.INTEGER,
413                                                    SimpleType.INTEGER, getStackTraceType()
414                                                  });
415          if (!(foundType.equals(new ArrayType(1, mType))))
416            throw new IllegalArgumentException("Field LockedMonitors is not of " +
417                                               "type " + mType.getClassName());
418        }
419    catch (OpenDataException e)
420      {
421        throw new IllegalStateException("Something went wrong in creating " +
422                                        "the composite data type for the " +
423                                        "object monitor information array.", e);
424      }
425    foundType = type.getType("LockedSynchronizers");
426    if (foundType != null)
427      try
428        {
429          CompositeType lType = new CompositeType(LockInfo.class.getName(),
430                                                  "Information on a lock",
431                                                  new String[] { "ClassName",
432                                                                 "IdentityHashCode"
433                                                  },
434                                                  new String[] { "Name of the class",
435                                                                 "Identity hash code " +
436                                                                 "of the class"
437                                                  },
438                                                  new OpenType[] {
439                                                    SimpleType.STRING, SimpleType.INTEGER
440                                                  });
441          if (!(foundType.equals(new ArrayType(1, lType))))
442            throw new IllegalArgumentException("Field LockedSynchronizers is not of " +
443                                               "type " + lType.getClassName());
444        }
445    catch (OpenDataException e)
446      {
447        throw new IllegalStateException("Something went wrong in creating " +
448                                        "the composite data type for the " +
449                                        "ownable synchronizerinformation array.", e);
450      }
451    CompositeData[] dTraces = (CompositeData[]) data.get("StackTrace");
452    StackTraceElement[] traces = new StackTraceElement[dTraces.length];
453    for (int a = 0; a < dTraces.length; ++a)
454        /* FIXME: We can't use the boolean as there is no available
455           constructor. */
456      traces[a] = 
457        new StackTraceElement((String) dTraces[a].get("ClassName"),
458                              (String) dTraces[a].get("MethodName"),
459                              (String) dTraces[a].get("FileName"),
460                              ((Integer) 
461                               dTraces[a].get("LineNumber")).intValue());
462    MonitorInfo[] mInfo;
463    if (data.containsKey("LockedMonitors"))
464      {
465        CompositeData[] dmInfos = (CompositeData[]) data.get("LockedMonitors");
466        mInfo = new MonitorInfo[dmInfos.length];
467        for (int a = 0; a < dmInfos.length; ++a)
468          mInfo[a] = MonitorInfo.from(dmInfos[a]);
469      }
470    else
471      mInfo = new MonitorInfo[]{};
472    LockInfo[] lInfo;
473    if (data.containsKey("LockedSynchronizers"))
474      {
475        CompositeData[] dlInfos = (CompositeData[]) data.get("LockedSynchronizers");
476        lInfo = new LockInfo[dlInfos.length];
477        for (int a = 0; a < dlInfos.length; ++a)
478          lInfo[a] = new LockInfo((String) dlInfos[a].get("ClassName"),
479                                  (Integer) dlInfos[a].get("IdentityHashCode"));
480      }
481    else
482      lInfo = new LockInfo[]{};
483    return new ThreadInfo(((Long) data.get("ThreadId")).longValue(),
484                          (String) data.get("ThreadName"),
485                          Thread.State.valueOf((String) data.get("ThreadState")),
486                          ((Long) data.get("BlockedCount")).longValue(),
487                          ((Long) data.get("BlockedTime")).longValue(),
488                          (String) data.get("LockName"),
489                          ((Long) data.get("LockOwnerId")).longValue(),
490                          (String) data.get("LockOwnerName"),  
491                          ((Long) data.get("WaitedCount")).longValue(),
492                          ((Long) data.get("WaitedTime")).longValue(),
493                          ((Boolean) data.get("InNative")).booleanValue(),
494                          ((Boolean) data.get("Suspended")).booleanValue(),
495                          traces, mInfo, lInfo);
496  }
497
498  /**
499   * Returns the number of times this thread has been
500   * in the {@link java.lang.Thread.State#BLOCKED} state.
501   * A thread enters this state when it is waiting to
502   * obtain an object's monitor.  This may occur either
503   * on entering a synchronized method for the first time,
504   * or on re-entering it following a call to
505   * {@link java.lang.Object#wait()}.
506   *
507   * @return the number of times this thread has been blocked.
508   */
509  public long getBlockedCount()
510  {
511    return blockedCount;
512  }
513
514  /**
515   * <p>
516   * Returns the accumulated number of milliseconds this
517   * thread has been in the
518   * {@link java.lang.Thread.State#BLOCKED} state
519   * since thread contention monitoring was last enabled.
520   * A thread enters this state when it is waiting to
521   * obtain an object's monitor.  This may occur either
522   * on entering a synchronized method for the first time,
523   * or on re-entering it following a call to
524   * {@link java.lang.Object#wait()}.
525   * </p>
526   * <p>
527   * Use of this method requires virtual machine support
528   * for thread contention monitoring and for this support
529   * to be enabled.
530   * </p>
531   * 
532   * @return the accumulated time (in milliseconds) that this
533   *         thread has spent in the blocked state, since
534   *         thread contention monitoring was enabled, or -1
535   *         if thread contention monitoring is disabled.
536   * @throws UnsupportedOperationException if the virtual
537   *                                       machine does not
538   *                                       support contention
539   *                                       monitoring.
540   * @see ThreadMXBean#isThreadContentionMonitoringEnabled()
541   * @see ThreadMXBean#isThreadContentionMonitoringSupported()
542   */
543  public long getBlockedTime()
544  {
545    if (bean == null)
546      bean = ManagementFactory.getThreadMXBean();
547    // Will throw UnsupportedOperationException for us
548    if (bean.isThreadContentionMonitoringEnabled())
549      return blockedTime;
550    else
551      return -1;
552  }
553
554  /**
555   * Returns an array of {@link MonitorInfo} objects representing
556   * information on the locks on object monitors held by the thread.
557   * If no locks are held, or such information was not requested
558   * on creating this {@link ThreadInfo} object, a zero-length
559   * array will be returned.
560   *
561   * @return information on object monitors locked by this thread.
562   */
563  public MonitorInfo[] getLockedMonitors()
564  {
565    return lockedMonitors;
566  }
567
568  /**
569   * Returns an array of {@link LockInfo} objects representing
570   * information on the locks on ownable synchronizers held by the thread.
571   * If no locks are held, or such information was not requested
572   * on creating this {@link ThreadInfo} object, a zero-length
573   * array will be returned.
574   *
575   * @return information on ownable synchronizers locked by this thread.
576   */
577  public LockInfo[] getLockedSynchronizers()
578  {
579    return lockedSynchronizers;
580  }
581
582  /**
583   * <p>
584   * Returns a {@link LockInfo} object representing the
585   * lock on which this thread is blocked.  If the thread
586   * is not blocked, this method returns <code>null</code>.
587   * </p>
588   * <p>
589   * The thread may be blocked due to one of three reasons:
590   * </p>
591   * <ol>
592   * <li>The thread is in the <code>BLOCKED</code> state
593   * waiting to acquire an object monitor in order to enter
594   * a synchronized method or block.</li>
595   * <li>The thread is in the <code>WAITING</code> or
596   * <code>TIMED_WAITING</code> state due to a call to
597   * {@link java.lang.Object#wait()}.</li>
598   * <li>The thread is in the <code>WAITING</code> or
599   * <code>TIMED_WAITING</code> state due to a call
600   * to {@link java.util.concurrent.locks.LockSupport#park()}.
601   * The lock is the return value of
602   * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.</li>
603   * </ol>
604   * 
605   * @return a {@link LockInfo} object representing the lock on
606   *         which the thread is blocked, or <code>null</code> if
607   *         the thread isn't blocked.
608   * @since 1.6
609   * @see #getLockName()
610   */
611  public LockInfo getLockInfo()
612  {
613    String lockName = getLockName();
614    int at = lockName.indexOf('@');
615    return new LockInfo(lockName.substring(0, at),
616                        Integer.decode(lockName.substring(at + 1)));
617  }
618
619  /**
620   * <p>
621   * Returns a {@link java.lang.String} representation of
622   * the lock on which this thread is blocked.  If
623   * the thread is not blocked, this method returns
624   * <code>null</code>.
625   * </p>
626   * <p>
627   * The returned {@link java.lang.String} is constructed
628   * using the class name and identity hashcode (usually
629   * the memory address of the object) of the lock.  The
630   * two are separated by the '@' character, and the identity
631   * hashcode is represented in hexadecimal.  Thus, for a
632   * lock, <code>l</code>, the returned value is
633   * the result of concatenating
634   * <code>l.getClass().getName()</code>, <code>"@"</code>
635   * and
636   * <code>Integer.toHexString(System.identityHashCode(l))</code>.
637   * The value is only unique to the extent that the identity
638   * hash code is also unique.  The value is the same as would
639   * be returned by <code>getLockInfo().toString()</code>
640   * </p>
641   *
642   * @return a string representing the lock on which this
643   *         thread is blocked, or <code>null</code> if
644   *         the thread is not blocked.
645   */
646  public String getLockName()
647  {
648    if (!isThreadBlocked())
649      return null;
650    return lockName;
651  }
652
653  /**
654   * Returns the identifier of the thread which owns the
655   * monitor lock this thread is waiting for.  -1 is returned
656   * if either this thread is not blocked, or the lock is
657   * not held by any other thread.
658   * 
659   * @return the thread identifier of thread holding the lock
660   *         this thread is waiting for, or -1 if the thread
661   *         is not blocked or the lock is not held by another
662   *         thread.
663   */
664  public long getLockOwnerId()
665  {
666    if (!isThreadBlocked())
667      return -1;
668    return lockOwnerId;
669  }
670
671  /**
672   * Returns the name of the thread which owns the
673   * monitor lock this thread is waiting for.  <code>null</code>
674   * is returned if either this thread is not blocked,
675   * or the lock is not held by any other thread.
676   * 
677   * @return the thread identifier of thread holding the lock
678   *         this thread is waiting for, or <code>null</code>
679   *         if the thread is not blocked or the lock is not
680   *         held by another thread.
681   */
682  public String getLockOwnerName()
683  {
684    if (!isThreadBlocked())
685      return null;
686    return lockOwnerName;
687  }
688
689  /**
690   * <p>
691   * Returns the stack trace of this thread to the depth
692   * specified on creation of this {@link ThreadInfo}
693   * object.  If the depth is zero, an empty array will
694   * be returned.  For non-zero arrays, the elements
695   * start with the most recent trace at position zero.
696   * The bottom of the stack represents the oldest method
697   * invocation which meets the depth requirements.
698   * </p>
699   * <p>
700   * Some virtual machines may not be able to return
701   * stack trace information for a thread.  In these
702   * cases, an empty array will also be returned.
703   * </p>
704   * 
705   * @return an array of {@link java.lang.StackTraceElement}s
706   *         representing the trace of this thread.
707   */
708  public StackTraceElement[] getStackTrace()
709  {
710    return trace;
711  }
712
713  /**
714   * Returns the identifier of the thread associated with
715   * this instance of {@link ThreadInfo}.
716   *
717   * @return the thread's identifier.
718   */
719  public long getThreadId()
720  {
721    return threadId;
722  }
723
724  /**
725   * Returns the name of the thread associated with
726   * this instance of {@link ThreadInfo}.
727   *
728   * @return the thread's name.
729   */
730  public String getThreadName()
731  {
732    return threadName;
733  }
734
735  /**
736   * Returns the state of the thread associated with
737   * this instance of {@link ThreadInfo}.
738   *
739   * @return the thread's state.
740   */
741  public Thread.State getThreadState()
742  {
743    return threadState;
744  }
745    
746  /**
747   * Returns the number of times this thread has been
748   * in the {@link java.lang.Thread.State#WAITING} 
749   * or {@link java.lang.Thread.State#TIMED_WAITING} state.
750   * A thread enters one of these states when it is waiting
751   * due to a call to {@link java.lang.Object.wait()},
752   * {@link java.lang.Object.join()} or
753   * {@link java.lang.concurrent.locks.LockSupport.park()},
754   * either with an infinite or timed delay, respectively. 
755   *
756   * @return the number of times this thread has been waiting.
757   */
758  public long getWaitedCount()
759  {
760    return waitedCount;
761  }
762
763  /**
764   * <p>
765   * Returns the accumulated number of milliseconds this
766   * thread has been in the
767   * {@link java.lang.Thread.State#WAITING} or
768   * {@link java.lang.Thread.State#TIMED_WAITING} state,
769   * since thread contention monitoring was last enabled.
770   * A thread enters one of these states when it is waiting
771   * due to a call to {@link java.lang.Object.wait()},
772   * {@link java.lang.Object.join()} or
773   * {@link java.lang.concurrent.locks.LockSupport.park()},
774   * either with an infinite or timed delay, respectively. 
775   * </p>
776   * <p>
777   * Use of this method requires virtual machine support
778   * for thread contention monitoring and for this support
779   * to be enabled.
780   * </p>
781   * 
782   * @return the accumulated time (in milliseconds) that this
783   *         thread has spent in one of the waiting states, since
784   *         thread contention monitoring was enabled, or -1
785   *         if thread contention monitoring is disabled.
786   * @throws UnsupportedOperationException if the virtual
787   *                                       machine does not
788   *                                       support contention
789   *                                       monitoring.
790   * @see ThreadMXBean#isThreadContentionMonitoringEnabled()
791   * @see ThreadMXBean#isThreadContentionMonitoringSupported()
792   */
793  public long getWaitedTime()
794  {
795    if (bean == null)
796      bean = ManagementFactory.getThreadMXBean();
797    // Will throw UnsupportedOperationException for us
798    if (bean.isThreadContentionMonitoringEnabled())
799      return waitedTime;
800    else
801      return -1;
802  }
803
804  /**
805   * Returns true if the thread is in a native method.  This
806   * excludes native code which forms part of the virtual
807   * machine itself, or which results from Just-In-Time
808   * compilation.
809   *
810   * @return true if the thread is in a native method, false
811   *         otherwise.
812   */
813  public boolean isInNative()
814  {
815    return isInNative;
816  }
817
818  /**
819   * Returns true if the thread has been suspended using
820   * {@link java.lang.Thread#suspend()}.
821   *
822   * @return true if the thread is suspended, false otherwise.
823   */
824  public boolean isSuspended()
825  {
826    return isSuspended;
827  }
828
829  /**
830   * Returns a {@link java.lang.String} representation of
831   * this {@link ThreadInfo} object.  This takes the form
832   * <code>java.lang.management.ThreadInfo[id=tid, name=n,
833   * state=s, blockedCount=bc, waitedCount=wc, isInNative=iin,
834   * isSuspended=is]</code>, where <code>tid</code> is
835   * the thread identifier, <code>n</code> is the
836   * thread name, <code>s</code> is the thread state,
837   * <code>bc</code> is the blocked state count,
838   * <code>wc</code> is the waiting state count and
839   * <code>iin</code> and <code>is</code> are boolean
840   * flags to indicate the thread is in native code or
841   * suspended respectively.  If the thread is blocked,
842   * <code>lock=l, lockOwner=lo</code> is also included,
843   * where <code>l</code> is the lock waited for, and
844   * <code>lo</code> is the thread which owns the lock
845   * (or null if there is no owner).
846   *
847   * @return the string specified above.
848   */
849  public String toString()
850  {
851    return getClass().getName() +
852      "[id=" + threadId + 
853      ", name=" + threadName +
854      ", state=" + threadState +
855      ", blockedCount=" + blockedCount +
856      ", waitedCount=" + waitedCount +
857      ", isInNative=" + isInNative + 
858      ", isSuspended=" + isSuspended +
859      (isThreadBlocked() ? 
860       ", lockOwnerId=" + lockOwnerId +
861       ", lockOwnerName=" + lockOwnerName : "") +
862      ", lockedMonitors=" + Arrays.toString(lockedMonitors) +
863      ", lockedSynchronizers=" + Arrays.toString(lockedSynchronizers) +
864      "]";
865  }
866
867  /**
868   * <p>
869   * Returns true if the thread is in a blocked state.
870   * The thread is regarded as blocked if:
871   * </p>
872   * <ol>
873   * <li>The thread is in the <code>BLOCKED</code> state
874   * waiting to acquire an object monitor in order to enter
875   * a synchronized method or block.</li>
876   * <li>The thread is in the <code>WAITING</code> or
877   * <code>TIMED_WAITING</code> state due to a call to
878   * {@link java.lang.Object#wait()}.</li>
879   * <li>The thread is in the <code>WAITING</code> or
880   * <code>TIMED_WAITING</code> state due to a call
881   * to {@link java.util.concurrent.locks.LockSupport#park()}.
882   * The lock is the return value of
883   * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.</li>
884   * </ol>
885   *
886   * @return true if the thread is blocked.
887   */
888  private boolean isThreadBlocked()
889  {
890    return (threadState == Thread.State.BLOCKED ||
891            threadState == Thread.State.WAITING ||
892            threadState == Thread.State.TIMED_WAITING);
893  }
894  
895}