From d67b8bdc46045d90b225292c55dee00d4cece77b Mon Sep 17 00:00:00 2001 From: Light2 Date: Mon, 4 Nov 2013 13:12:54 +0100 Subject: [PATCH] Added deadlock detection --- src/main/NGECore.java | 15 ++ src/resources/common/ThreadMonitor.java | 226 ++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 src/resources/common/ThreadMonitor.java diff --git a/src/main/NGECore.java b/src/main/NGECore.java index 0fffeea3..fc11b9f1 100644 --- a/src/main/NGECore.java +++ b/src/main/NGECore.java @@ -34,6 +34,7 @@ import java.util.concurrent.ConcurrentHashMap; import resources.common.RadialOptions; +import resources.common.ThreadMonitor; import resources.objects.creature.CreatureObject; import services.AttributeService; import services.BuffService; @@ -145,6 +146,20 @@ public class NGECore { public void start() { instance = this; + + final ThreadMonitor deadlockDetector = new ThreadMonitor(); + Thread deadlockMonitor = new Thread(new Runnable() { + @Override + public void run() { + try { + deadlockDetector.findDeadlock(); + Thread.sleep(60000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + deadlockMonitor.start(); config = new Config(); config.setFilePath("nge.cfg"); if (!(config.loadConfigFile())) { diff --git a/src/resources/common/ThreadMonitor.java b/src/resources/common/ThreadMonitor.java new file mode 100644 index 00000000..43adcb64 --- /dev/null +++ b/src/resources/common/ThreadMonitor.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2013 + * + * This File is part of NGECore2. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Using NGEngine to work with NGECore2 is making a combined work based on NGEngine. + * Therefore all terms and conditions of the GNU Lesser General Public License cover the combination. + ******************************************************************************/ +package resources.common; + +import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME; +import static java.lang.management.ManagementFactory.getThreadMXBean; +import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy; + +import java.io.IOException; +import java.lang.management.LockInfo; +import java.lang.management.MonitorInfo; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +public class ThreadMonitor { + + private MBeanServerConnection server; + + private ThreadMXBean tmbean; + + private ObjectName objname; + + // default - JDK 6+ VM + private String findDeadlocksMethodName = "findDeadlockedThreads"; + + private boolean canDumpLocks = true; + + /** + * Constructs a ThreadMonitor object to get thread information in the local + * JVM. + */ + public ThreadMonitor() { + this.tmbean = getThreadMXBean(); + } + + /** + * Prints the thread dump information to System.out. + */ + public void threadDump() { + if (canDumpLocks) { + if (tmbean.isObjectMonitorUsageSupported() && tmbean.isSynchronizerUsageSupported()) { + dumpThreadInfoWithLocks(); + } + } else { + dumpThreadInfo(); + } + } + + private void dumpThreadInfo() { + System.out.println("Full Java thread dump"); + long[] tids = tmbean.getAllThreadIds(); + ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE); + for (ThreadInfo ti : tinfos) { + printThreadInfo(ti); + } + } + + /** + * Prints the thread dump information with locks info to System.out. + */ + private void dumpThreadInfoWithLocks() { + System.out.println("Full Java thread dump with locks info"); + + ThreadInfo[] tinfos = tmbean.dumpAllThreads(true, true); + for (ThreadInfo ti : tinfos) { + printThreadInfo(ti); + LockInfo[] syncs = ti.getLockedSynchronizers(); + printLockInfo(syncs); + } + System.out.println(); + } + + private static String INDENT = " "; + + private void printThreadInfo(ThreadInfo ti) { + // print thread information + printThread(ti); + + // print stack trace with locks + StackTraceElement[] stacktrace = ti.getStackTrace(); + MonitorInfo[] monitors = ti.getLockedMonitors(); + for (int i = 0; i < stacktrace.length; i++) { + StackTraceElement ste = stacktrace[i]; + System.out.println(INDENT + "at " + ste.toString()); + for (MonitorInfo mi : monitors) { + if (mi.getLockedStackDepth() == i) { + System.out.println(INDENT + " - locked " + mi); + } + } + } + System.out.println(); + } + + private void printThread(ThreadInfo ti) { + StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" + " Id=" + + ti.getThreadId() + " in " + ti.getThreadState()); + if (ti.getLockName() != null) { + sb.append(" on lock=" + ti.getLockName()); + } + if (ti.isSuspended()) { + sb.append(" (suspended)"); + } + if (ti.isInNative()) { + sb.append(" (running in native)"); + } + System.out.println(sb.toString()); + if (ti.getLockOwnerName() != null) { + System.out.println(INDENT + " owned by " + ti.getLockOwnerName() + " Id=" + + ti.getLockOwnerId()); + } + } + + private void printMonitorInfo(ThreadInfo ti, MonitorInfo[] monitors) { + System.out.println(INDENT + "Locked monitors: count = " + monitors.length); + for (MonitorInfo mi : monitors) { + System.out.println(INDENT + " - " + mi + " locked at "); + System.out.println(INDENT + " " + mi.getLockedStackDepth() + " " + + mi.getLockedStackFrame()); + } + } + + private void printLockInfo(LockInfo[] locks) { + System.out.println(INDENT + "Locked synchronizers: count = " + locks.length); + for (LockInfo li : locks) { + System.out.println(INDENT + " - " + li); + } + System.out.println(); + } + + /** + * Checks if any threads are deadlocked. If any, print the thread dump + * information. + */ + public boolean findDeadlock() { + long[] tids; + if (findDeadlocksMethodName.equals("findDeadlockedThreads") + && tmbean.isSynchronizerUsageSupported()) { + tids = tmbean.findDeadlockedThreads(); + if (tids == null) { + return false; + } + + System.out.println("Deadlock found :-"); + ThreadInfo[] infos = tmbean.getThreadInfo(tids, true, true); + for (ThreadInfo ti : infos) { + printThreadInfo(ti); + printLockInfo(ti.getLockedSynchronizers()); + System.out.println(); + } + } else { + tids = tmbean.findMonitorDeadlockedThreads(); + if (tids == null) { + return false; + } + ThreadInfo[] infos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE); + for (ThreadInfo ti : infos) { + // print thread information + printThreadInfo(ti); + } + } + + return true; + } + + private void parseMBeanInfo() throws IOException { + try { + MBeanOperationInfo[] mopis = server.getMBeanInfo(objname).getOperations(); + + // look for findDeadlockedThreads operations; + boolean found = false; + for (MBeanOperationInfo op : mopis) { + if (op.getName().equals(findDeadlocksMethodName)) { + found = true; + break; + } + } + if (!found) { + findDeadlocksMethodName = "findMonitorDeadlockedThreads"; + canDumpLocks = false; + } + } catch (IntrospectionException e) { + InternalError ie = new InternalError(e.getMessage()); + ie.initCause(e); + throw ie; + } catch (InstanceNotFoundException e) { + InternalError ie = new InternalError(e.getMessage()); + ie.initCause(e); + throw ie; + } catch (ReflectionException e) { + InternalError ie = new InternalError(e.getMessage()); + ie.initCause(e); + throw ie; + } + } + } \ No newline at end of file