/* * Copyright (c) 2008-2012, Hazel Bilisim Ltd. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.impl.management; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; /** * ThreadDump Java 1.6 implementation */ class ThreadDumpGeneratorImpl_16 extends ThreadDumpGenerator { private static final String ThreadMXBean_isObjectMonitorUsageSupported = "isObjectMonitorUsageSupported"; private static final String ThreadMXBean_isSynchronizerUsageSupported = "isSynchronizerUsageSupported"; private static final String ThreadMXBean_dumpAllThreads = "dumpAllThreads"; private static final String ThreadMXBean_getThreadInfo = "getThreadInfo"; private static final String ThreadMXBean_findDeadlockedThreads = "findDeadlockedThreads"; private static final String ThreadInfo_getLockInfo = "getLockInfo"; private static final String ThreadInfo_getLockedSynchronizers = "getLockedSynchronizers"; private static final String ThreadInfo_getLockedMonitors = "getLockedMonitors"; private static final String MonitorInfo_getLockedStackDepth = "getLockedStackDepth"; private final static ConcurrentMap<String, Method> methods = new ConcurrentHashMap<String, Method>(); public ThreadDumpGeneratorImpl_16(ThreadMXBean bean) { super(bean); } public ThreadInfo[] getAllThreads() { if (booleanCall(threadMxBean, ThreadMXBean_isObjectMonitorUsageSupported) && booleanCall(threadMxBean, ThreadMXBean_isSynchronizerUsageSupported)) { return parameterizedObjectCall(threadMxBean, ThreadMXBean_dumpAllThreads, new Class[]{boolean.class, boolean.class}, new Object[]{true, true}); } else { return super.getAllThreads(); } } public ThreadInfo[] findDeadlockedThreads() { if (booleanCall(threadMxBean, ThreadMXBean_isSynchronizerUsageSupported)) { long[] tids = objectCall(threadMxBean, ThreadMXBean_findDeadlockedThreads); if (tids == null || tids.length == 0) { return null; } return parameterizedObjectCall(threadMxBean, ThreadMXBean_getThreadInfo, new Class[]{long[].class, boolean.class, boolean.class}, new Object[]{tids, true, true}); } else { return super.findDeadlockedThreads(); } } /** * copied from JDK 1.6 {@link ThreadInfo} toString() */ protected void appendThreadInfo(ThreadInfo info, StringBuilder sb) { sb.append("\"").append(info.getThreadName()).append("\"").append( " Id=").append(info.getThreadId()).append(" ").append( info.getThreadState()); if (info.getLockName() != null) { sb.append(" on ").append(info.getLockName()); } if (info.getLockOwnerName() != null) { sb.append(" owned by \"").append(info.getLockOwnerName()). append("\" Id=").append(+info.getLockOwnerId()); } if (info.isSuspended()) { sb.append(" (suspended)"); } if (info.isInNative()) { sb.append(" (in native)"); } sb.append('\n'); final StackTraceElement[] stackTrace = info.getStackTrace(); final Object lockInfo = objectCall(info, ThreadInfo_getLockInfo); final Object[] monitorInfo = objectCall(info, ThreadInfo_getLockedMonitors); for (int i = 0; i < stackTrace.length; i++) { StackTraceElement ste = stackTrace[i]; sb.append("\tat ").append(ste.toString()); sb.append('\n'); if (i == 0 && lockInfo != null) { Thread.State ts = info.getThreadState(); switch (ts) { case BLOCKED: sb.append("\t- blocked on ").append(lockInfo); sb.append('\n'); break; case WAITING: sb.append("\t- waiting on ").append(lockInfo); sb.append('\n'); break; case TIMED_WAITING: sb.append("\t- waiting on ").append(lockInfo); sb.append('\n'); break; default: } } for (Object mi : monitorInfo) { Integer depth = objectCall(mi, MonitorInfo_getLockedStackDepth); if (depth == i) { sb.append("\t- locked ").append(mi); sb.append('\n'); } } } final Object[] locks = objectCall(info, ThreadInfo_getLockedSynchronizers); if (locks.length > 0) { sb.append("\n\tNumber of locked synchronizers = ").append(locks.length); sb.append('\n'); for (Object li : locks) { sb.append("\t- ").append(li); sb.append('\n'); } } sb.append('\n'); } private static boolean booleanCall(Object object, String methodName) { Boolean result = objectCall(object, methodName); return result != null ? result.booleanValue() : false; } private static <T extends Object> T objectCall(Object object, String methodName) { return (T) parameterizedObjectCall(object, methodName, null, null); } private static <T extends Object> T parameterizedObjectCall(Object object, String methodName, Class[] types, Object[] params) { if (object == null) { throw new NullPointerException("Object is mandatory!"); } try { Class clazz = object.getClass(); String mKey = clazz.getName() + "." + methodName; Method m = methods.get(mKey); if (m == null) { m = object.getClass().getMethod(methodName, types); m.setAccessible(true); final Method anotherMethod = methods.putIfAbsent(mKey, m); if (anotherMethod != null) { m = anotherMethod; } } return (T) m.invoke(object, params); } catch (Exception e) { logger.log(Level.SEVERE, "While invoking method[" + methodName + "] of class[" + object.getClass().getName() + "]", e); } return null; } }