/*
* Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The
* University of Hong Kong (HKU). All Rights Reserved.
*
* This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1]
*
* [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
package hk.hku.cecid.piazza.commons.swallow;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
/**
* The <code>DiagnosticUtilities</code> is is a utility for providing useful information for monitoring and
* debugging the JVM.
*
* @author Twinsen Tsang
* @version 1.0.0
* @since JDK5.0, H2O 0908
*/
public class DiagnosticUtilities
{
// The private singleton object.
private static DiagnosticUtilities singleton;
/**
* Get the single instance of <code>DiagnosticUtilities</code>.
*
* @return the single instance of <code>DiagnosticUtilities</code>.
*/
public static DiagnosticUtilities getInstance()
{
synchronized(DiagnosticUtilities.class)
{
if (singleton == null)
{
/*
* RESERVED FOR DISCOVERY PATTERN
*/
singleton = new DiagnosticUtilities();
}
}
return singleton;
}
/**
* Get the new instance of <code>DiagnosticUtilities</code>.
*
* @return the new instance of <code>DiagnosticUtilities</code>.
*/
public static DiagnosticUtilities getNewInstance()
{
return new DiagnosticUtilities();
}
/**
* Dump all threads information from the current JVM.
*/
public void dumpAllThread() throws IOException
{
this.dumpAllThread(System.out, 3);
}
/**
* Dump all threads information from the current JVM to the specified OutputStream
* <code>os</code>.
*
* @param os The output stream to dump the threads information.
* @param stackTraceDepth The depth of stack trace to dump, default is 3.
* @throws NullPointerException when <code>os</code> is null.
* @throws IllegalArgumentException when <code>stackTraceDepth</code> less than zero.
*/
public void dumpAllThread(OutputStream os, int stackTraceDepth) throws IOException
{
/*
* TODO: Support of dumping thread priority, is daemon?
*/
if (os == null)
{
throw new NullPointerException("Missing 'os' in the arugments.");
}
os.write(this.dumpAllThread0(stackTraceDepth).getBytes());
os.flush();
}
/**
* Dump all threads information from the current JVM to the specified OutputStream
* <code>w</code>.
*
* @param w The writer used to dump the threads information.
* @param stackTraceDepth The depth of stack trace to dump, default is 3.
* @throws NullPointerException when <code>os</code> is null.
* @throws IllegalArgumentException when <code>stackTraceDepth</code> less than zero.
*/
public void dumpAllThread(Writer w, int stackTraceDepth) throws IOException
{
/*
* TODO: Support of dumping thread priority, is daemon?
*/
if (w == null)
{
throw new NullPointerException("Missing 'writer' in the arugments.");
}
w.write(this.dumpAllThread0(stackTraceDepth));
w.flush();
}
/*
* Dump all threads information from the current JVM and return as String.
*
* The threads information is on the basis of the JMX thread bean in the default
* MBean server.
*
* @return a string containing all threads information.
*/
private String dumpAllThread0(int stackTraceDepth)
{
if (stackTraceDepth < 0)
{
throw new IllegalArgumentException("'stackTraceDepth' must greater than zero.");
}
StringBuilder whole = new StringBuilder();
ThreadMXBean threadBeans = ManagementFactory.getThreadMXBean();
long [] tids = threadBeans.getAllThreadIds();
for (int i = 0; i < tids.length; i++)
{
// Collect threadInfo from the MXBean.
ThreadInfo tinfo = threadBeans.getThreadInfo(tids[i], stackTraceDepth);
/*
* Translate each information with prefix.
*/
String lock = this.replaceEmpty("lk=" , tinfo.getLockName());
String lockOwnerName = this.replaceEmpty("lkowner=" , tinfo.getLockOwnerName());
String lockId = tinfo.getLockOwnerId() == -1 ? "" : String.valueOf(tinfo.getLockOwnerId());
String blockCount = this.replaceEmpty("bkCount=" , Long.valueOf(tinfo.getBlockedCount()));
String waitCount = this.replaceEmpty("wtCount=" , Long.valueOf(tinfo.getWaitedCount()));
String state = this.getResolvedThreadState(tinfo);
StringBuilder sb = new StringBuilder()
.append("\"" + tinfo.getThreadName() + "\" ")
.append("tid=")
.append(tinfo.getThreadId())
.append(" ")
.append(state)
.append(" ")
.append(lock)
.append(lockOwnerName)
.append(lockId)
.append(blockCount)
.append(waitCount)
.append("\n");
/*
* Output stack track element if exist.
*/
StackTraceElement[] sts = tinfo.getStackTrace();
for (int j = 0; j < sts.length; j++)
{
sb.append(" [" + j + "] ")
.append(sts[j])
.append("\n");
}
sb.append("\n");
whole.append(sb);
}
return whole.toString();
}
private String replaceEmpty(String prefix, Object obj)
{
if (obj == null) return "";
if (obj instanceof String)
{
return prefix + (String)obj + " ";
}
return prefix + obj.toString() + " ";
}
/*
* Get a resolved thread state from a entry of ThreadInfo according
* to the following syntax.
*
* resolved state = is thread in native code ? IS_IN_NATIVE(thread's state)
* or
* resolved state = is thread suspended ? SUSPENDED(thread's state)
* else
* resolved state = thread state.
*/
private String getResolvedThreadState(ThreadInfo tinfo)
{
String state = tinfo.getThreadState().toString();
if (tinfo.isInNative())
{
state = "IS_IN_NATIVE(" + state + ")";
}
else if (tinfo.isSuspended())
{
state = "SUSPENDED(" + state + ")";
}
return state;
}
/*
* (FOR TESTING ONLY)
*/
public static void main(String[] args) throws IOException
{
DiagnosticUtilities.getInstance().dumpAllThread();
}
}