package org.limewire.util;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
/** Simple class to help monitor deadlocking. */
public class DeadlockMonitor {
/**
* How often to check for deadlocks.
*
* This class doubles as a workaround for bug_id=6435126,
* so it doesn't use a multiple of 10 for the sleep interval.
*/
private static final int DEADLOCK_CHECK_INTERVAL = 3001;
public static void startDeadlockMonitoring() {
Thread t = new Thread(new Runnable() {
public void run() {
while(true) {
try {
Thread.sleep(DEADLOCK_CHECK_INTERVAL);
} catch (InterruptedException ignored) {}
long [] ids = findDeadlockedThreads(ManagementFactory.getThreadMXBean());
if (ids == null) {
continue;
}
StringBuilder sb = new StringBuilder("Deadlock Report:\n");
StackTraceElement[] firstStackTrace = null;
ThreadInfo[] allThreadInfo = getThreadInfo(ids);
for (ThreadInfo info : allThreadInfo) {
sb.append("\"" + info.getThreadName() + "\" (id=" + info.getThreadId() + ")");
sb.append(" " + info.getThreadState() + " on " + info.getLockName() + " owned by ");
sb.append("\"" + info.getLockOwnerName() + "\" (id=" + info.getLockOwnerId() + ")");
if (info.isSuspended())
sb.append(" (suspended)");
if (info.isInNative())
sb.append(" (in native)");
sb.append("\n");
StackTraceElement[] trace = info.getStackTrace();
if(firstStackTrace == null)
firstStackTrace = trace;
for(int i = 0; i < trace.length; i++) {
sb.append("\tat " + trace[i].toString() + "\n");
if(i == 0)
addLockInfo(info, sb);
addMonitorInfo(info, sb, i);
}
addLockedSynchronizers(info, sb);
sb.append("\n");
}
Exception deadlock = new Exception();
// Redirect the stack trace to separate deadlock reports.
if(firstStackTrace != null)
deadlock.setStackTrace(firstStackTrace);
System.err.println("DEADLOCK!\n" + sb.toString());
return;
}
}
});
t.setDaemon(true);
t.setName("JUnit Test Deadlock Detection Thread");
t.start();
}
/** Uses reflection to add locked synchronizers data. */
private static void addLockedSynchronizers(ThreadInfo info, StringBuilder sb) {
try {
Method m = ThreadInfo.class.getMethod("getLockedSynchronizers");
Object o = m.invoke(info);
if(o != null) {
int length = Array.getLength(o);
if(length > 0) {
sb.append("\n\tNumber of locked synchronizers = " + length + "\n");
for(int i = 0; i < length; i++)
sb.append("\t- " + Array.get(o, i) + "\n");
}
}
} catch(Throwable t) {
}
}
/** Uses reflection to add more specific locking details. */
private static void addMonitorInfo(ThreadInfo info, StringBuilder sb, int stackDepth) {
try {
Method m = ThreadInfo.class.getMethod("getLockedMonitors");
Object o = m.invoke(info);
if(o != null) {
Class<?> monitorInfoClass = Class.forName("java.lang.management.MonitorInfo");
int length = Array.getLength(o);
for(int i = 0; i < length; i++) {
Object mi = Array.get(o, i);
Method depthMethod = monitorInfoClass.getMethod("getLockedStackDepth");
Object depth = depthMethod.invoke(mi);
if(depth != null && depth.equals(Integer.valueOf(stackDepth)))
sb.append("\t- locked " + mi + "\n");
}
}
} catch(Throwable t) {
}
}
/** Uses reflection to add the LockInfo data to the report. */
private static void addLockInfo(ThreadInfo info, StringBuilder sb) {
try {
Method m = ThreadInfo.class.getMethod("getLockInfo");
Object o = m.invoke(info);
if(o != null) {
Thread.State ts = info.getThreadState();
switch (ts) {
case BLOCKED:
sb.append("\t- blocked on " + o + "\n");
break;
case WAITING:
case TIMED_WAITING:
sb.append("\t- waiting on " + o + "\n");
break;
default:
}
}
} catch(Throwable t) {
}
}
/** Uses reflection to run the Java 1.6 method 'findDeadlockedThreads' on a bean. */
private static long[] findDeadlockedThreads(ThreadMXBean bean) {
try {
Method m = ThreadMXBean.class.getMethod("findDeadlockedThreads");
Object o = m.invoke(bean);
if(o instanceof long[] || o == null)
return (long[])o;
} catch(Throwable t) {
}
// fallback to the monitor'd one if anything bad happens.
return bean.findMonitorDeadlockedThreads();
}
/** Uses reflection to get ThreadInfo with monitors & synchronizers. */
private static ThreadInfo[] getThreadInfo(long[] ids) {
try {
Method m = ThreadMXBean.class.getDeclaredMethod("getThreadInfo", new Class[] { long[].class, boolean.class, boolean.class } );
Object o = m.invoke(ManagementFactory.getThreadMXBean(), new Object[] { ids, true, true } );
return (ThreadInfo[])o;
} catch (Throwable t) {
}
//fallback to retrieving info w/o monitor & synchronizer info
return ManagementFactory.getThreadMXBean().getThreadInfo(ids, Integer.MAX_VALUE);
}
}