package org.spigotmc; import java.lang.management.ManagementFactory; import java.lang.management.MonitorInfo; import java.lang.management.ThreadInfo; import java.util.logging.Level; import java.util.logging.Logger; import net.minecraft.server.MinecraftServer; import org.bukkit.Bukkit; public class WatchdogThread extends Thread { private static WatchdogThread instance; private final long timeoutTime; private final boolean restart; private volatile long lastTick; private volatile boolean stopping; private WatchdogThread(long timeoutTime, boolean restart) { super( "Torch Watchdog Thread" ); this.timeoutTime = timeoutTime; this.restart = restart; } public static void doStart(int timeoutTime, boolean restart) { if ( instance == null ) { instance = new WatchdogThread( timeoutTime * 1000L, restart ); instance.start(); } } public static void tick() { instance.lastTick = System.currentTimeMillis(); } public static void doStop() { if ( instance != null ) { instance.stopping = true; } } @Override public void run() { while ( !stopping ) { // if ( lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable { Logger log = Bukkit.getServer().getLogger(); log.log( Level.SEVERE, "The server has stopped responding!" ); log.log( Level.SEVERE, "Please report this to https://github.com/TorchSpigot/Torch/issues" ); log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" ); log.log( Level.SEVERE, "Torch version: " + Bukkit.getServer().getVersion() ); // if(net.minecraft.server.World.haveWeSilencedAPhysicsCrash) { log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed" ); log.log( Level.SEVERE, "near " + net.minecraft.server.World.blockLocation); } // Paper start - Warn in watchdog if an excessive velocity was ever set if ( org.bukkit.craftbukkit.CraftServer.excessiveVelEx != null ) { log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity" ); log.log( Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated" ); log.log( Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage()); for ( StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace() ) { log.log( Level.SEVERE, "\t\t" + stack ); } } // Paper end log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE ), log ); log.log( Level.SEVERE, "------------------------------" ); // log.log( Level.SEVERE, "Entire Thread Dump:" ); ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true ); for ( ThreadInfo thread : threads ) { dumpThread( thread, log ); } log.log( Level.SEVERE, "------------------------------" ); if ( restart ) { RestartCommand.restart(); } break; } try { sleep( 10000 ); } catch ( InterruptedException ex ) { interrupt(); } } } private static void dumpThread(ThreadInfo thread, Logger log) { log.log( Level.SEVERE, "------------------------------" ); // log.log( Level.SEVERE, "Current Thread: " + thread.getThreadName() ); log.log( Level.SEVERE, "\tPID: " + thread.getThreadId() + " | Suspended: " + thread.isSuspended() + " | Native: " + thread.isInNative() + " | State: " + thread.getThreadState() ); if ( thread.getLockedMonitors().length != 0 ) { log.log( Level.SEVERE, "\tThread is waiting on monitor(s):" ); for ( MonitorInfo monitor : thread.getLockedMonitors() ) { log.log( Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame() ); } } log.log( Level.SEVERE, "\tStack:" ); // for ( StackTraceElement stack : thread.getStackTrace() ) { log.log( Level.SEVERE, "\t\t" + stack ); } } }