diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ThreadDumpUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ThreadDumpUtil.java new file mode 100644 index 0000000000..2d942615c3 --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ThreadDumpUtil.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.activemq.artemis.utils; + +import org.apache.activemq.artemis.logs.ActiveMQUtilLogger; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +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; + +public final class ThreadDumpUtil { + + public static String threadDump(final String msg) { + + try ( + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str) + ) { + + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + + out.println("*******************************************************************************"); + out.println("Complete Thread dump " + msg); + + for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads(true, true)) { + out.println(threadInfoToString(threadInfo)); + } + + long[] deadlockedThreads = threadMXBean.findDeadlockedThreads(); + + if (deadlockedThreads != null && deadlockedThreads.length > 0) { + out.println("Deadlock detected!"); + out.println(); + + for (ThreadInfo threadInfo : threadMXBean.getThreadInfo(deadlockedThreads, true, true)) { + out.println(threadInfoToString(threadInfo)); + } + } + + out.println("==============================================================================="); + out.println("End Thread dump " + msg); + out.println("*******************************************************************************"); + + return str.toString(); + + } catch (IOException e) { + ActiveMQUtilLogger.LOGGER.error("Exception thrown during generating of thread dump.", e); + } + + return "Generating of thread dump failed " + msg; + + } + + private static String threadInfoToString(ThreadInfo threadInfo) { + StringBuilder sb = new StringBuilder("\"" + threadInfo.getThreadName() + "\"" + + " Id=" + threadInfo.getThreadId() + " " + + threadInfo.getThreadState()); + if (threadInfo.getLockName() != null) { + sb.append(" on " + threadInfo.getLockName()); + } + if (threadInfo.getLockOwnerName() != null) { + sb.append(" owned by \"" + threadInfo.getLockOwnerName() + + "\" Id=" + threadInfo.getLockOwnerId()); + } + if (threadInfo.isSuspended()) { + sb.append(" (suspended)"); + } + if (threadInfo.isInNative()) { + sb.append(" (in native)"); + } + sb.append('\n'); + int i = 0; + for (; i < threadInfo.getStackTrace().length; i++) { + StackTraceElement ste = threadInfo.getStackTrace()[i]; + sb.append("\tat " + ste.toString()); + sb.append('\n'); + if (i == 0 && threadInfo.getLockInfo() != null) { + Thread.State ts = threadInfo.getThreadState(); + switch (ts) { + case BLOCKED: + sb.append("\t- blocked on " + threadInfo.getLockInfo()); + sb.append('\n'); + break; + case WAITING: + sb.append("\t- waiting on " + threadInfo.getLockInfo()); + sb.append('\n'); + break; + case TIMED_WAITING: + sb.append("\t- waiting on " + threadInfo.getLockInfo()); + sb.append('\n'); + break; + default: + } + } + + for (MonitorInfo mi : threadInfo.getLockedMonitors()) { + if (mi.getLockedStackDepth() == i) { + sb.append("\t- locked " + mi); + sb.append('\n'); + } + } + } + + LockInfo[] locks = threadInfo.getLockedSynchronizers(); + if (locks.length > 0) { + sb.append("\n\tNumber of locked synchronizers = " + locks.length); + sb.append('\n'); + for (LockInfo li : locks) { + sb.append("\t- " + li); + sb.append('\n'); + } + } + sb.append('\n'); + return sb.toString(); + } + +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java index fff7a57648..03c80104fd 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java @@ -169,6 +169,7 @@ import org.apache.activemq.artemis.utils.CompositeAddress; import org.apache.activemq.artemis.utils.ExecutorFactory; import org.apache.activemq.artemis.utils.ReusableLatch; import org.apache.activemq.artemis.utils.SecurityFormatter; +import org.apache.activemq.artemis.utils.ThreadDumpUtil; import org.apache.activemq.artemis.utils.TimeUtils; import org.apache.activemq.artemis.utils.VersionLoader; import org.apache.activemq.artemis.utils.actors.OrderedExecutorFactory; @@ -925,28 +926,7 @@ public class ActiveMQServerImpl implements ActiveMQServer { @Override public void threadDump() { - StringWriter str = new StringWriter(); - PrintWriter out = new PrintWriter(str); - - Map stackTrace = Thread.getAllStackTraces(); - - out.println(ActiveMQMessageBundle.BUNDLE.generatingThreadDump()); - out.println("*******************************************************************************"); - - for (Map.Entry el : stackTrace.entrySet()) { - out.println("==============================================================================="); - out.println(ActiveMQMessageBundle.BUNDLE.threadDump(el.getKey(), el.getKey().getName(), el.getKey().getId(), el.getKey().getThreadGroup())); - out.println(); - for (StackTraceElement traceEl : el.getValue()) { - out.println(traceEl); - } - } - - out.println("==============================================================================="); - out.println(ActiveMQMessageBundle.BUNDLE.endThreadDump()); - out.println("*******************************************************************************"); - - ActiveMQServerLogger.LOGGER.threadDump(str.toString()); + ActiveMQServerLogger.LOGGER.threadDump(ThreadDumpUtil.threadDump("")); } @Override diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java index ba9d805c30..a63eec7e03 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java @@ -34,8 +34,6 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.lang.management.ManagementFactory; import java.lang.ref.WeakReference; import java.net.ServerSocket; @@ -138,6 +136,7 @@ import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; import org.apache.activemq.artemis.utils.Env; import org.apache.activemq.artemis.utils.FileUtil; +import org.apache.activemq.artemis.utils.ThreadDumpUtil; import org.apache.activemq.artemis.utils.actors.OrderedExecutorFactory; import org.apache.activemq.artemis.utils.RandomUtil; import org.apache.activemq.artemis.utils.ThreadLeakCheckRule; @@ -619,34 +618,9 @@ public abstract class ActiveMQTestBase extends Assert { } public static String threadDump(final String msg) { - StringWriter str = new StringWriter(); - PrintWriter out = new PrintWriter(str); - Map stackTrace = Thread.getAllStackTraces(); + return ThreadDumpUtil.threadDump(msg); - out.println("*******************************************************************************"); - out.println("Complete Thread dump " + msg); - - for (Map.Entry el : stackTrace.entrySet()) { - out.println("==============================================================================="); - out.println("Thread " + el.getKey() + - " name = " + - el.getKey().getName() + - " id = " + - el.getKey().getId() + - " group = " + - el.getKey().getThreadGroup()); - out.println(); - for (StackTraceElement traceEl : el.getValue()) { - out.println(traceEl); - } - } - - out.println("==============================================================================="); - out.println("End Thread dump " + msg); - out.println("*******************************************************************************"); - - return str.toString(); } /**