001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 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   * Explicit serialVersionUID.
035   */
036  private static final long serialVersionUID = 1L;
037  /**
038   * Logger for this class.
039   */
040  private static Logger log = LoggerFactory.getLogger(ThreadInformation.class);
041  /**
042   * Determines whether the debug level is enabled in the log configuration, without the cost of a method call.
043   */
044  private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
045  /**
046   * The id of this thread.
047   */
048  private final long id;
049  /**
050   * The name of this thread.
051   */
052  private final String name;
053  /**
054   * The state of this thread.
055   */
056  private final Thread.State state;
057  /**
058   * The stack trace of this thread.
059   */
060  private final List<StackFrameInformation> stackTrace;
061  /**
062   * The ownable synchronizers held by this thread.
063   */
064  private final List<LockInformation> ownableSynchronizers;
065  /**
066   * The count of times this thread has been waiting.
067   */
068  private final long waitCount;
069  /**
070   * The total cumulated wait time.
071   */
072  private final long waitTime;
073  /**
074   * The count of times this thread has been blocked.
075   */
076  private final long blockedCount;
077  /**
078   * The total cumulated block time.
079   */
080  private final long blockedTime;
081  /**
082   * Whether this thread is uspended.
083   */
084  private final boolean suspended;
085  /**
086   * Whether this thread is in native code.
087   */
088  private final boolean inNative;
089  /**
090   * The the lock this thread is waiting for, if any.
091   */
092  private final LockInformation lockInformation;
093  /**
094   * The id of the owner of the lock this thread is waiting for, if any.
095   */
096  private final long lockOwnerId;
097
098  /**
099   * Initialize this object from the specified {@link ThreadInfo}.
100   * @param ti the <code>ThreadInfo</code> from which to get the information.
101   */
102  public ThreadInformation(final ThreadInfo ti) {
103    this.id = ti.getThreadId();
104    this.name = ti.getThreadName();
105    this.state = ti.getThreadState();
106    this.waitCount = ti.getWaitedCount();
107    this.waitTime = ti.getWaitedTime();
108    this.blockedCount = ti.getBlockedCount();
109    this.blockedTime = ti.getBlockedTime();
110    this.suspended = ti.isSuspended();
111    this.inNative = ti.isInNative();
112    this.lockOwnerId = ti.getLockOwnerId();
113    final LockInfo linfo = ti.getLockInfo();
114    this.lockInformation = linfo != null ? new LockInformation(linfo) : null;
115    this.stackTrace = fillStackTrace(ti);
116    final LockInfo[] sync = ti.getLockedSynchronizers();
117    if (sync.length > 0) {
118      ownableSynchronizers = new ArrayList<>();
119      for (final LockInfo li: sync) ownableSynchronizers.add(new LockInformation(li));
120      if (debugEnabled) log.debug("thread '" + name + "' ownable synchronizers: " + ownableSynchronizers);
121    } else {
122      ownableSynchronizers = null;
123      if (debugEnabled) log.debug("thread '" + name + "' has no ownable synchronizer");
124    }
125  }
126
127  /**
128   * Fill the stack trace information for this thread.
129   * @param ti the ThreadInfo from hich to get the information.
130   * @return a list of {@link StackFrameInformation} objects.
131   */
132  private static List<StackFrameInformation> fillStackTrace(final ThreadInfo ti) {
133    final StackTraceElement[] ste = ti.getStackTrace();
134    if (ste.length <= 0) return null;
135    final List<StackFrameInformation> result = new ArrayList<>();
136    final SortedMap<Integer, LockInformation> lockInfoMap = new TreeMap<>();
137    for (final MonitorInfo mi: ti.getLockedMonitors()) {
138      final int idx = mi.getLockedStackDepth();
139      if (idx >= 0) lockInfoMap.put(idx, new LockInformation(mi));
140    }
141    for (int i = 0; i < ste.length; i++)
142      result.add(new StackFrameInformation(ste[i], lockInfoMap.get(i)));
143    return result;
144  }
145
146  /**
147   * Get the id of this thread.
148   * @return the id as a long.
149   */
150  public long getId() {
151    return id;
152  }
153
154  /**
155   * Get the name of this thread.
156   * @return the name as a string.
157   */
158  public String getName() {
159    return name;
160  }
161
162  /**
163   * Get the state of this thread.
164   * @return a {@link java.lang.Thread.State Thread.State} enum value.
165   */
166  public Thread.State getState() {
167    return state;
168  }
169
170  /**
171   * Get the stack trace of this thread.
172   * @return a list of {@link StackFrameInformation} elements, or <code>null</code> if no stack trace is available.
173   */
174  public List<StackFrameInformation> getStackTrace() {
175    return stackTrace;
176  }
177
178  /**
179   * Get the ownable synchronizers held by this thread.
180   * @return a list of {@link LockInformation}, or null if this thread holds no ownable synchrnizer.
181   */
182  public List<LockInformation> getOwnableSynchronizers() {
183    return ownableSynchronizers;
184  }
185
186  /**
187   * Get the count of times this thread has been waiting.
188   * @return the wait count as a long.
189   */
190  public long getWaitCount() {
191    return waitCount;
192  }
193
194  /**
195   * Get the total cumulated wait time.
196   * @return the wait time as a long.
197   */
198  public long getWaitTime() {
199    return waitTime;
200  }
201
202  /**
203   * Get the count of times this thread has been blocked.
204   * @return the blocked count as a long.
205   */
206  public long getBlockedCount() {
207    return blockedCount;
208  }
209
210  /**
211   * Get the total cumulated block time.
212   * @return the blocked time as a long.
213   */
214  public long getBlockedTime() {
215    return blockedTime;
216  }
217
218  /**
219   * Get whether this thread is suspended.
220   * @return <code>true</code> if this thread is suspended, <code>false</code> otherwise.
221   */
222  public boolean isSuspended() {
223    return suspended;
224  }
225
226  /**
227   * Get whether this thread is in native code.
228   * @return <code>true</code> if this thread is in native code, <code>false</code> otherwise.
229   */
230  public boolean isInNative() {
231    return inNative;
232  }
233
234  /**
235   * Get the lock this thread is waiting for, if any.
236   * @return a {@link LockInformation} instance, or <code>null</code> if this thread is not waiting on a lock.
237   */
238  public LockInformation getLockInformation() {
239    return lockInformation;
240  }
241
242  /**
243   * Get the id of the owner of the lock this thread is waiting for, if any.
244   * @return the owner thread id as positive long value, or -1 if this thread is not waiting on a lock.
245   */
246  public long getLockOwnerId() {
247    return lockOwnerId;
248  }
249}