001/*
002 * JPPF.
003 * Copyright (C) 2005-2015 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.management.diagnostics;
020
021import java.io.Serializable;
022import java.lang.management.*;
023import java.util.*;
024
025import org.jppf.utils.LoggingUtils;
026import org.slf4j.*;
027
028/**
029 * Information about a thread, including the stack trace and associated locks.
030 * @author Laurent Cohen
031 */
032public class ThreadInformation implements Serializable
033{
034  /**
035   * Logger for this class.
036   */
037  private static Logger log = LoggerFactory.getLogger(ThreadInformation.class);
038  /**
039   * Determines whether the debug level is enabled in the log configuration, without the cost of a method call.
040   */
041  private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
042  /**
043   * The id of this thread.
044   */
045  private final long id;
046  /**
047   * The name of this thread.
048   */
049  private final String name;
050  /**
051   * The state of this thread.
052   */
053  private final Thread.State state;
054  /**
055   * The stack trace of this thread.
056   */
057  private final List<StackFrameInformation> stackTrace;
058  /**
059   * The ownable synchronizers held by this thread.
060   */
061  private final List<LockInformation> ownableSynchronizers;
062  /**
063   * The count of times this thread has been waiting.
064   */
065  private final long waitCount;
066  /**
067   * The total cumulated wait time.
068   */
069  private final long waitTime;
070  /**
071   * The count of times this thread has been blocked.
072   */
073  private final long blockedCount;
074  /**
075   * The total cumulated block time.
076   */
077  private final long blockedTime;
078  /**
079   * Whether this thread is uspended.
080   */
081  private final boolean suspended;
082  /**
083   * Whether this thread is in native code.
084   */
085  private final boolean inNative;
086  /**
087   * The the lock this thread is waiting for, if any.
088   */
089  private final LockInformation lockInformation;
090  /**
091   * The id of the owner of the lock this thread is waiting for, if any.
092   */
093  private final long lockOwnerId;
094
095  /**
096   * Initialize this object from the specified {@link ThreadInfo}.
097   * @param ti the <code>ThreadInfo</code> from which to get the information.
098   */
099  public ThreadInformation(final ThreadInfo ti)
100  {
101    this.id = ti.getThreadId();
102    this.name = ti.getThreadName();
103    this.state = ti.getThreadState();
104    this.waitCount = ti.getWaitedCount();
105    this.waitTime = ti.getWaitedTime();
106    this.blockedCount = ti.getBlockedCount();
107    this.blockedTime = ti.getBlockedTime();
108    this.suspended = ti.isSuspended();
109    this.inNative = ti.isInNative();
110    this.lockOwnerId = ti.getLockOwnerId();
111    LockInfo linfo = ti.getLockInfo();
112    this.lockInformation = linfo != null ? new LockInformation(linfo) : null;
113    this.stackTrace = fillStackTrace(ti);
114    LockInfo[] sync = ti.getLockedSynchronizers();
115    if (sync.length > 0)
116    {
117      ownableSynchronizers = new ArrayList<>();
118      for (LockInfo li: sync) ownableSynchronizers.add(new LockInformation(li));
119      if (debugEnabled) log.debug("thread '" + name + "' ownable synchronizers: " + ownableSynchronizers);
120    }
121    else
122    {
123      ownableSynchronizers = null;
124      if (debugEnabled) log.debug("thread '" + name + "' has no ownable synchronizer");
125    }
126  }
127
128  /**
129   * Fill the stack trace information for this thread.
130   * @param ti the ThreadInfo from hich to get the information.
131   * @return a list of {@link StackFrameInformation} objects.
132   */
133  private List<StackFrameInformation> fillStackTrace(final ThreadInfo ti)
134  {
135    StackTraceElement[] ste = ti.getStackTrace();
136    if (ste.length <= 0) return null;
137    List<StackFrameInformation> result = new ArrayList<>();
138    SortedMap<Integer, LockInformation> lockInfoMap = new TreeMap<>();
139    for (MonitorInfo mi: ti.getLockedMonitors())
140    {
141      int idx = mi.getLockedStackDepth();
142      if (idx >= 0) lockInfoMap.put(idx, new LockInformation(mi));
143    }
144    for (int i=0; i<ste.length; i++) result.add(new StackFrameInformation(ste[i], lockInfoMap.get(i)));
145    return result;
146  }
147
148  /**
149   * Get the id of this thread.
150   * @return the id as a long.
151   */
152  public long getId()
153  {
154    return id;
155  }
156
157  /**
158   * Get the name of this thread.
159   * @return the name as a string.
160   */
161  public String getName()
162  {
163    return name;
164  }
165
166  /**
167   * Get the state of this thread.
168   * @return a {@link java.lang.Thread.State Thread.State} enum value.
169   */
170  public Thread.State getState()
171  {
172    return state;
173  }
174
175  /**
176   * Get the stack trace of this thread.
177   * @return a list of {@link StackFrameInformation} elements, or <code>null</code> if no stack trace is available.
178   */
179  public List<StackFrameInformation> getStackTrace()
180  {
181    return stackTrace;
182  }
183
184  /**
185   * Get the ownable synchronizers held by this thread.
186   * @return a list of {@link LockInformation}, or null if this thread holds no ownable synchrnizer.
187   */
188  public List<LockInformation> getOwnableSynchronizers()
189  {
190    return ownableSynchronizers;
191  }
192
193  /**
194   * Get the count of times this thread has been waiting.
195   * @return the wait count as a long.
196   */
197  public long getWaitCount()
198  {
199    return waitCount;
200  }
201
202  /**
203   * Get the total cumulated wait time.
204   * @return the wait time as a long.
205   */
206  public long getWaitTime()
207  {
208    return waitTime;
209  }
210
211  /**
212   * Get the count of times this thread has been blocked.
213   * @return the blocked count as a long.
214   */
215  public long getBlockedCount()
216  {
217    return blockedCount;
218  }
219
220  /**
221   * Get the total cumulated block time.
222   * @return the blocked time as a long.
223   */
224  public long getBlockedTime()
225  {
226    return blockedTime;
227  }
228
229  /**
230   * Get whether this thread is suspended.
231   * @return <code>true</code> if this thread is suspended, <code>false</code> otherwise.
232   */
233  public boolean isSuspended()
234  {
235    return suspended;
236  }
237
238  /**
239   * Get whether this thread is in native code.
240   * @return <code>true</code> if this thread is in native code, <code>false</code> otherwise.
241   */
242  public boolean isInNative()
243  {
244    return inNative;
245  }
246
247  /**
248   * Get the lock this thread is waiting for, if any.
249   * @return a {@link LockInformation} instance, or <code>null</code> if this thread is not waiting on a lock.
250   */
251  public LockInformation getLockInformation()
252  {
253    return lockInformation;
254  }
255
256  /**
257   * Get the id of the owner of the lock this thread is waiting for, if any.
258   * @return the owner thread id as positive long value, or -1 if this thread is not waiting on a lock.
259   */
260  public long getLockOwnerId()
261  {
262    return lockOwnerId;
263  }
264}