package org.limewire.util; import java.lang.management.LockInfo; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; /** Utilities relating to threads. */ public class ThreadUtils { /** Returns all stack traces, including lock info. */ public static String getAllStackTraces() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); boolean monitor = threadMXBean.isObjectMonitorUsageSupported(); boolean sync = threadMXBean.isSynchronizerUsageSupported(); ThreadInfo[] allThreadInfo = threadMXBean.dumpAllThreads(monitor, sync); StringBuilder sb = new StringBuilder("Stack Trace Report:\n"); buildTrace(allThreadInfo, sb); return sb.toString(); } /** Builds the stack traces of all the given threadIDs in the buffer. Returns the stack trace of the first thread. */ public static StackTraceElement[] buildStackTraces(long[] threadIds, StringBuilder buffer) { ThreadInfo[] allThreadInfo = ManagementFactory.getThreadMXBean().getThreadInfo(threadIds, true, true); return buildTrace(allThreadInfo, buffer); } /** Builds the stack traces of all the given ThreadInfos in the buffer. Returns the stack trace of the first thread. */ private static StackTraceElement[] buildTrace(ThreadInfo[] allThreadInfo, StringBuilder buffer) { StackTraceElement[] firstStackTrace = null; for (ThreadInfo info : allThreadInfo) { buffer.append("\"" + info.getThreadName() + "\" (id=" + info.getThreadId() + ")"); buffer.append(" " + info.getThreadState() + " on " + info.getLockName() + " owned by "); buffer.append("\"" + info.getLockOwnerName() + "\" (id=" + info.getLockOwnerId() + ")"); if (info.isSuspended()) buffer.append(" (suspended)"); if (info.isInNative()) buffer.append(" (in native)"); buffer.append("\n"); StackTraceElement[] trace = info.getStackTrace(); if(firstStackTrace == null) firstStackTrace = trace; for(int i = 0; i < trace.length; i++) { buffer.append("\tat " + trace[i].toString() + "\n"); if(i == 0) addLockInfo(info, buffer); addMonitorInfo(info, buffer, i); } addLockedSynchronizers(info, buffer); buffer.append("\n"); } return firstStackTrace; } /** Returns the thread id of all potentially locked threads. */ public static long[] findDeadlockedThreads() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); if(threadMXBean.isSynchronizerUsageSupported()) { return threadMXBean.findDeadlockedThreads(); } else { return threadMXBean.findMonitorDeadlockedThreads(); } } /** Add locked synchronizers data. */ private static void addLockedSynchronizers(ThreadInfo info, StringBuilder sb) { LockInfo[] lockInfo = info.getLockedSynchronizers(); if(lockInfo.length > 0) { sb.append("\n\tNumber of locked synchronizers = " + lockInfo.length + "\n"); for(int i = 0; i < lockInfo.length; i++) { sb.append("\t- " + lockInfo[i] + "\n"); } } } /** Add more specific locking details. */ private static void addMonitorInfo(ThreadInfo info, StringBuilder sb, int stackDepth) { MonitorInfo[] monitorInfos = info.getLockedMonitors(); for(int i = 0; i < monitorInfos.length; i++) { MonitorInfo mi = monitorInfos[i]; int depth = mi.getLockedStackDepth(); if(depth == stackDepth) { sb.append("\t- locked " + mi + "\n"); } } } /** Add the LockInfo data to the report. */ private static void addLockInfo(ThreadInfo info, StringBuilder sb) { Thread.State ts = info.getThreadState(); switch (ts) { case BLOCKED: sb.append("\t- blocked on " + info.getLockInfo() + "\n"); break; case WAITING: case TIMED_WAITING: sb.append("\t- waiting on " + info.getLockInfo() + "\n"); break; default: } } }