mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-21 18:15:54 +01:00
[[Experimental]] Rewrite Watchdog thread / crash detector.
This commit is contained in:
parent
421ce6cf7d
commit
f7687b0c20
272
CraftBukkit-Patches/0030-Watchdog-Thread.patch
Normal file
272
CraftBukkit-Patches/0030-Watchdog-Thread.patch
Normal file
@ -0,0 +1,272 @@
|
||||
From 6c8c769a97f22a7d9d5f8fa7692ae087dd717497 Mon Sep 17 00:00:00 2001
|
||||
From: md_5 <md_5@live.com.au>
|
||||
Date: Sat, 23 Feb 2013 12:33:20 +1100
|
||||
Subject: [PATCH] Watchdog Thread.
|
||||
|
||||
---
|
||||
.../java/net/minecraft/server/MinecraftServer.java | 2 +
|
||||
src/main/java/org/bukkit/craftbukkit/Spigot.java | 64 ++++++++++++++-
|
||||
src/main/java/org/spigotmc/RestartCommand.java | 23 ++++++
|
||||
src/main/java/org/spigotmc/WatchdogThread.java | 93 ++++++++++++++++++++++
|
||||
4 files changed, 180 insertions(+), 2 deletions(-)
|
||||
create mode 100644 src/main/java/org/spigotmc/RestartCommand.java
|
||||
create mode 100644 src/main/java/org/spigotmc/WatchdogThread.java
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 128016f..3a6b620 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -418,6 +418,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
|
||||
lastTick = curTime;
|
||||
MinecraftServer.currentTick++;
|
||||
this.q();
|
||||
+ org.spigotmc.WatchdogThread.tick();
|
||||
}
|
||||
// Spigot end
|
||||
} else {
|
||||
@@ -445,6 +446,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
|
||||
this.a(crashreport);
|
||||
} finally {
|
||||
try {
|
||||
+ org.spigotmc.WatchdogThread.doStop();
|
||||
this.stop();
|
||||
this.isStopped = true;
|
||||
} catch (Throwable throwable1) {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/Spigot.java b/src/main/java/org/bukkit/craftbukkit/Spigot.java
|
||||
index 5729cd6..6a4cbac 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/Spigot.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/Spigot.java
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.bukkit.craftbukkit;
|
||||
|
||||
+import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import net.minecraft.server.*;
|
||||
@@ -7,16 +8,20 @@ import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
-import java.util.logging.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
+import org.bukkit.entity.Player;
|
||||
import org.spigotmc.Metrics;
|
||||
+import org.spigotmc.RestartCommand;
|
||||
+import org.spigotmc.WatchdogThread;
|
||||
|
||||
public class Spigot {
|
||||
+
|
||||
public static boolean tabPing = false;
|
||||
private static Metrics metrics;
|
||||
|
||||
public static void initialize(CraftServer server, SimpleCommandMap commandMap, YamlConfiguration configuration) {
|
||||
commandMap.register("bukkit", new org.bukkit.craftbukkit.command.TicksPerSecondCommand("tps"));
|
||||
+ commandMap.register("restart", new RestartCommand("restart"));
|
||||
|
||||
server.whitelistMessage = configuration.getString("settings.whitelist-message", server.whitelistMessage);
|
||||
server.stopMessage = configuration.getString("settings.stop-message", server.stopMessage);
|
||||
@@ -26,12 +31,21 @@ public class Spigot {
|
||||
server.spamGuardExclusions = configuration.getStringList("settings.spam-exclusions");
|
||||
server.mapSendInterval = configuration.getInt("settings.map-send-interval", server.mapSendInterval);
|
||||
|
||||
+ int configVersion = configuration.getInt("config-version");
|
||||
+ switch (configVersion) {
|
||||
+ case 0:
|
||||
+ configuration.set("settings.timeout-time", 30);
|
||||
+ }
|
||||
+ configuration.set("config-version", 1);
|
||||
+
|
||||
+ WatchdogThread.doStart(configuration.getInt("settings.timeout-time", 30), configuration.getBoolean("settings.restart-on-crash", false));
|
||||
+
|
||||
server.orebfuscatorEnabled = configuration.getBoolean("orebfuscator.enable", false);
|
||||
server.orebfuscatorEngineMode = configuration.getInt("orebfuscator.engine-mode", 1);
|
||||
server.orebfuscatorUpdateRadius = configuration.getInt("orebfuscator.update-radius", 2);
|
||||
server.orebfuscatorDisabledWorlds = configuration.getStringList("orebfuscator.disabled-worlds");
|
||||
if (server.orebfuscatorEngineMode != 1 && server.orebfuscatorEngineMode != 2) {
|
||||
- server.orebfuscatorEngineMode = 1;
|
||||
+ server.orebfuscatorEngineMode = 1;
|
||||
}
|
||||
|
||||
if (server.chunkGCPeriod == 0) {
|
||||
@@ -194,4 +208,50 @@ public class Spigot {
|
||||
return (entity instanceof EntityArrow && !((EntityArrow) entity).inGround);
|
||||
|
||||
}
|
||||
+
|
||||
+ public static void restart() {
|
||||
+ try {
|
||||
+ String startupScript = MinecraftServer.getServer().server.configuration.getString("settings.restart-script-location", "");
|
||||
+ File file = new File(startupScript);
|
||||
+ if (file.isFile()) {
|
||||
+ System.out.println("Attempting to restart with " + startupScript);
|
||||
+
|
||||
+ // Kick all players
|
||||
+ for (Player p : Bukkit.getServer().getOnlinePlayers()) {
|
||||
+ ((org.bukkit.craftbukkit.entity.CraftPlayer) p).kickPlayer("Server is restarting", true);
|
||||
+ }
|
||||
+ // Give the socket a chance to send the packets
|
||||
+ try {
|
||||
+ Thread.sleep(100);
|
||||
+ } catch (InterruptedException ex) {
|
||||
+ }
|
||||
+ // Close the socket so we can rebind with the new process
|
||||
+ MinecraftServer.getServer().ae().a();
|
||||
+
|
||||
+ // Give time for it to kick in
|
||||
+ try {
|
||||
+ Thread.sleep(100);
|
||||
+ } catch (InterruptedException ex) {
|
||||
+ }
|
||||
+
|
||||
+ // Actually shutdown
|
||||
+ try {
|
||||
+ MinecraftServer.getServer().stop();
|
||||
+ } catch (Throwable t) {
|
||||
+ }
|
||||
+
|
||||
+ String os = System.getProperty("os.name").toLowerCase();
|
||||
+ if (os.contains("win")) {
|
||||
+ Runtime.getRuntime().exec("cmd /c start " + file.getPath());
|
||||
+ } else {
|
||||
+ Runtime.getRuntime().exec(file.getPath());
|
||||
+ }
|
||||
+ System.exit(0);
|
||||
+ } else {
|
||||
+ System.out.println("Startup script '" + startupScript + "' does not exist!");
|
||||
+ }
|
||||
+ } catch (Exception ex) {
|
||||
+ ex.printStackTrace();
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
|
||||
new file mode 100644
|
||||
index 0000000..2d5c89f
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/spigotmc/RestartCommand.java
|
||||
@@ -0,0 +1,23 @@
|
||||
+package org.spigotmc;
|
||||
+
|
||||
+import org.bukkit.command.Command;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.Spigot;
|
||||
+
|
||||
+public class RestartCommand extends Command {
|
||||
+
|
||||
+ public RestartCommand(String name) {
|
||||
+ super(name);
|
||||
+ this.description = "Restarts the server";
|
||||
+ this.usageMessage = "/restart";
|
||||
+ this.setPermission("bukkit.command.restart");
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean execute(CommandSender sender, String currentAlias, String[] args) {
|
||||
+ if (testPermission(sender)) {
|
||||
+ Spigot.restart();
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
new file mode 100644
|
||||
index 0000000..10390b8
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -0,0 +1,93 @@
|
||||
+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 org.bukkit.Bukkit;
|
||||
+import org.bukkit.craftbukkit.Spigot;
|
||||
+
|
||||
+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("Spigot 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) {
|
||||
+ Logger log = Bukkit.getServer().getLogger();
|
||||
+ log.log(Level.SEVERE, "The server has stopped responding!");
|
||||
+ log.log(Level.SEVERE, "Please report this to http://www.spigotmc.org/");
|
||||
+ log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
|
||||
+ log.log(Level.SEVERE, "Spigot version: " + Bukkit.getServer().getVersion());
|
||||
+ //
|
||||
+ log.log(Level.SEVERE, "Current Thread State:");
|
||||
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
|
||||
+ for (ThreadInfo thread : threads) {
|
||||
+ if (thread.getThreadState() != State.WAITING) {
|
||||
+ 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:");
|
||||
+ //
|
||||
+ StackTraceElement[] stack = thread.getStackTrace();
|
||||
+ for (int line = 0; line < stack.length; line++) {
|
||||
+ log.log(Level.SEVERE, "\t\t" + stack[line].toString());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ log.log(Level.SEVERE, "------------------------------");
|
||||
+
|
||||
+ if (restart) {
|
||||
+ Spigot.restart();
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ sleep(10000);
|
||||
+ } catch (InterruptedException ex) {
|
||||
+ interrupt();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
--
|
||||
1.8.1-rc2
|
||||
|
Loading…
Reference in New Issue
Block a user