Add Early Warning Feature to WatchDog

Detect when the server has been hung for a long duration, and start printing
thread dumps at an interval until the point of crash.

This will help diagnose what was going on in that time before the crash.
This commit is contained in:
miclebrick
2018-08-08 15:30:52 -04:00
parent 2f4d83a219
commit 31fc02af68
5 changed files with 77 additions and 42 deletions

View File

@@ -822,7 +822,7 @@
protected void runServer() { protected void runServer() {
try { try {
if (!this.initServer()) { if (!this.initServer()) {
@@ -727,9 +1116,15 @@ @@ -727,9 +1116,16 @@
} }
this.nextTickTimeNanos = Util.getNanos(); this.nextTickTimeNanos = Util.getNanos();
@@ -831,6 +831,7 @@
this.status = this.buildServerStatus(); this.status = this.buildServerStatus();
+ // Spigot start + // Spigot start
+ org.spigotmc.WatchdogThread.hasStarted = true; // Paper
+ Arrays.fill( this.recentTps, 20 ); + Arrays.fill( this.recentTps, 20 );
+ // Paper start - further improve server tick loop + // Paper start - further improve server tick loop
+ long tickSection = Util.getNanos(); + long tickSection = Util.getNanos();
@@ -839,7 +840,7 @@
while (this.running) { while (this.running) {
long i; long i;
@@ -744,11 +1139,30 @@ @@ -744,11 +1140,30 @@
if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) { if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) {
long k = j / i; long k = j / i;
@@ -870,7 +871,7 @@
boolean flag = i == 0L; boolean flag = i == 0L;
@@ -757,6 +1171,8 @@ @@ -757,6 +1172,8 @@
this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount); this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
} }
@@ -879,7 +880,7 @@
this.nextTickTimeNanos += i; this.nextTickTimeNanos += i;
try { try {
@@ -830,6 +1246,13 @@ @@ -830,6 +1247,13 @@
this.services.profileCache().clearExecutor(); this.services.profileCache().clearExecutor();
} }
@@ -893,7 +894,7 @@
this.onServerExit(); this.onServerExit();
} }
@@ -889,9 +1312,16 @@ @@ -889,9 +1313,16 @@
} }
private boolean haveTime() { private boolean haveTime() {
@@ -911,7 +912,7 @@
public static boolean throwIfFatalException() { public static boolean throwIfFatalException() {
RuntimeException runtimeexception = (RuntimeException) MinecraftServer.fatalException.get(); RuntimeException runtimeexception = (RuntimeException) MinecraftServer.fatalException.get();
@@ -903,7 +1333,7 @@ @@ -903,7 +1334,7 @@
} }
public static void setFatalException(RuntimeException exception) { public static void setFatalException(RuntimeException exception) {
@@ -920,7 +921,7 @@
} }
@Override @Override
@@ -977,7 +1407,7 @@ @@ -977,7 +1408,7 @@
} }
} }
@@ -929,7 +930,7 @@
Profiler.get().incrementCounter("runTask"); Profiler.get().incrementCounter("runTask");
super.doRunTask(ticktask); super.doRunTask(ticktask);
} }
@@ -1025,6 +1455,7 @@ @@ -1025,6 +1456,7 @@
} }
public void tickServer(BooleanSupplier shouldKeepTicking) { public void tickServer(BooleanSupplier shouldKeepTicking) {
@@ -937,7 +938,7 @@
long i = Util.getNanos(); long i = Util.getNanos();
int j = this.pauseWhileEmptySeconds() * 20; int j = this.pauseWhileEmptySeconds() * 20;
@@ -1041,6 +1472,7 @@ @@ -1041,6 +1473,7 @@
this.autoSave(); this.autoSave();
} }
@@ -945,7 +946,7 @@
this.tickConnection(); this.tickConnection();
return; return;
} }
@@ -1055,12 +1487,13 @@ @@ -1055,12 +1488,13 @@
} }
--this.ticksUntilAutosave; --this.ticksUntilAutosave;
@@ -960,7 +961,7 @@
gameprofilerfiller.push("tallying"); gameprofilerfiller.push("tallying");
long k = Util.getNanos() - i; long k = Util.getNanos() - i;
int l = this.tickCount % 100; int l = this.tickCount % 100;
@@ -1074,7 +1507,7 @@ @@ -1074,7 +1508,7 @@
} }
private void autoSave() { private void autoSave() {
@@ -969,7 +970,7 @@
MinecraftServer.LOGGER.debug("Autosave started"); MinecraftServer.LOGGER.debug("Autosave started");
ProfilerFiller gameprofilerfiller = Profiler.get(); ProfilerFiller gameprofilerfiller = Profiler.get();
@@ -1123,7 +1556,7 @@ @@ -1123,7 +1557,7 @@
private ServerStatus buildServerStatus() { private ServerStatus buildServerStatus() {
ServerStatus.Players serverping_serverpingplayersample = this.buildPlayerStatus(); ServerStatus.Players serverping_serverpingplayersample = this.buildPlayerStatus();
@@ -978,7 +979,7 @@
} }
private ServerStatus.Players buildPlayerStatus() { private ServerStatus.Players buildPlayerStatus() {
@@ -1133,7 +1566,7 @@ @@ -1133,7 +1567,7 @@
if (this.hidesOnlinePlayers()) { if (this.hidesOnlinePlayers()) {
return new ServerStatus.Players(i, list.size(), List.of()); return new ServerStatus.Players(i, list.size(), List.of());
} else { } else {
@@ -987,7 +988,7 @@
ObjectArrayList<GameProfile> objectarraylist = new ObjectArrayList(j); ObjectArrayList<GameProfile> objectarraylist = new ObjectArrayList(j);
int k = Mth.nextInt(this.random, 0, list.size() - j); int k = Mth.nextInt(this.random, 0, list.size() - j);
@@ -1154,24 +1587,43 @@ @@ -1154,24 +1588,43 @@
this.getPlayerList().getPlayers().forEach((entityplayer) -> { this.getPlayerList().getPlayers().forEach((entityplayer) -> {
entityplayer.connection.suspendFlushing(); entityplayer.connection.suspendFlushing();
}); });
@@ -1031,7 +1032,7 @@
gameprofilerfiller.push("tick"); gameprofilerfiller.push("tick");
@@ -1186,6 +1638,7 @@ @@ -1186,6 +1639,7 @@
gameprofilerfiller.pop(); gameprofilerfiller.pop();
gameprofilerfiller.pop(); gameprofilerfiller.pop();
@@ -1039,7 +1040,7 @@
} }
gameprofilerfiller.popPush("connection"); gameprofilerfiller.popPush("connection");
@@ -1265,8 +1718,24 @@ @@ -1265,8 +1719,24 @@
@Nullable @Nullable
public ServerLevel getLevel(ResourceKey<Level> key) { public ServerLevel getLevel(ResourceKey<Level> key) {
return (ServerLevel) this.levels.get(key); return (ServerLevel) this.levels.get(key);
@@ -1064,7 +1065,7 @@
public Set<ResourceKey<Level>> levelKeys() { public Set<ResourceKey<Level>> levelKeys() {
return this.levels.keySet(); return this.levels.keySet();
} }
@@ -1296,7 +1765,7 @@ @@ -1296,7 +1766,7 @@
@DontObfuscate @DontObfuscate
public String getServerModName() { public String getServerModName() {
@@ -1073,7 +1074,7 @@
} }
public SystemReport fillSystemReport(SystemReport details) { public SystemReport fillSystemReport(SystemReport details) {
@@ -1347,7 +1816,7 @@ @@ -1347,7 +1817,7 @@
@Override @Override
public void sendSystemMessage(Component message) { public void sendSystemMessage(Component message) {
@@ -1082,7 +1083,7 @@
} }
public KeyPair getKeyPair() { public KeyPair getKeyPair() {
@@ -1481,10 +1950,20 @@ @@ -1481,10 +1951,20 @@
@Override @Override
public String getMotd() { public String getMotd() {
@@ -1104,7 +1105,7 @@
this.motd = motd; this.motd = motd;
} }
@@ -1507,7 +1986,7 @@ @@ -1507,7 +1987,7 @@
} }
public ServerConnectionListener getConnection() { public ServerConnectionListener getConnection() {
@@ -1113,7 +1114,7 @@
} }
public boolean isReady() { public boolean isReady() {
@@ -1634,11 +2113,11 @@ @@ -1634,11 +2114,11 @@
public CompletableFuture<Void> reloadResources(Collection<String> dataPacks) { public CompletableFuture<Void> reloadResources(Collection<String> dataPacks) {
CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> { CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> {
@@ -1127,7 +1128,7 @@
}, this).thenCompose((immutablelist) -> { }, this).thenCompose((immutablelist) -> {
MultiPackResourceManager resourcemanager = new MultiPackResourceManager(PackType.SERVER_DATA, immutablelist); MultiPackResourceManager resourcemanager = new MultiPackResourceManager(PackType.SERVER_DATA, immutablelist);
List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(resourcemanager, this.registries.compositeAccess()); List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(resourcemanager, this.registries.compositeAccess());
@@ -1654,6 +2133,7 @@ @@ -1654,6 +2134,7 @@
}).thenAcceptAsync((minecraftserver_reloadableresources) -> { }).thenAcceptAsync((minecraftserver_reloadableresources) -> {
this.resources.close(); this.resources.close();
this.resources = minecraftserver_reloadableresources; this.resources = minecraftserver_reloadableresources;
@@ -1135,7 +1136,7 @@
this.packRepository.setSelected(dataPacks); this.packRepository.setSelected(dataPacks);
WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()); WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures());
@@ -1952,7 +2432,7 @@ @@ -1952,7 +2433,7 @@
final List<String> list = Lists.newArrayList(); final List<String> list = Lists.newArrayList();
final GameRules gamerules = this.getGameRules(); final GameRules gamerules = this.getGameRules();
@@ -1144,7 +1145,7 @@
@Override @Override
public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) { public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
list.add(String.format(Locale.ROOT, "%s=%s\n", key.getId(), gamerules.getRule(key))); list.add(String.format(Locale.ROOT, "%s=%s\n", key.getId(), gamerules.getRule(key)));
@@ -2058,7 +2538,7 @@ @@ -2058,7 +2539,7 @@
try { try {
label51: label51:
{ {
@@ -1153,7 +1154,7 @@
try { try {
arraylist = Lists.newArrayList(NativeModuleLister.listModules()); arraylist = Lists.newArrayList(NativeModuleLister.listModules());
@@ -2108,6 +2588,21 @@ @@ -2108,6 +2589,21 @@
} }
@@ -1175,7 +1176,7 @@
private ProfilerFiller createProfiler() { private ProfilerFiller createProfiler() {
if (this.willStartRecordingMetrics) { if (this.willStartRecordingMetrics) {
this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()), Util.timeSource, Util.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> { this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()), Util.timeSource, Util.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> {
@@ -2225,18 +2720,24 @@ @@ -2225,18 +2721,24 @@
} }
public void logChatMessage(Component message, ChatType.Bound params, @Nullable String prefix) { public void logChatMessage(Component message, ChatType.Bound params, @Nullable String prefix) {

View File

@@ -142,7 +142,7 @@
thread.setDaemon(true); thread.setDaemon(true);
thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER));
thread.start(); thread.start();
@@ -126,13 +203,26 @@ @@ -126,13 +203,27 @@
this.setPreventProxyConnections(dedicatedserverproperties.preventProxyConnections); this.setPreventProxyConnections(dedicatedserverproperties.preventProxyConnections);
this.setLocalIp(dedicatedserverproperties.serverIp); this.setLocalIp(dedicatedserverproperties.serverIp);
} }
@@ -156,6 +156,7 @@
+ this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess()); + this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
+ this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess()); + this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
+ // Paper end - initialize global and world-defaults configuration + // Paper end - initialize global and world-defaults configuration
+ org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash); // Paper - start watchdog thread
+ io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
+ com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
+ com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
@@ -170,7 +171,7 @@
DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode); DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode);
InetAddress inetaddress = null; InetAddress inetaddress = null;
@@ -156,21 +246,34 @@ @@ -156,21 +247,34 @@
return false; return false;
} }
@@ -208,7 +209,7 @@
this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList()); this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList());
this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME); this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME);
long i = Util.getNanos(); long i = Util.getNanos();
@@ -178,13 +281,13 @@ @@ -178,13 +282,13 @@
SkullBlockEntity.setup(this.services, this); SkullBlockEntity.setup(this.services, this);
GameProfileCache.setUsesAuthentication(this.usesAuthentication()); GameProfileCache.setUsesAuthentication(this.usesAuthentication());
DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName()); DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName());
@@ -224,7 +225,7 @@
} }
if (dedicatedserverproperties.enableQuery) { if (dedicatedserverproperties.enableQuery) {
@@ -197,7 +300,7 @@ @@ -197,7 +301,7 @@
this.rconThread = RconThread.create(this); this.rconThread = RconThread.create(this);
} }
@@ -233,7 +234,7 @@
Thread thread1 = new Thread(new ServerWatchdog(this)); Thread thread1 = new Thread(new ServerWatchdog(this));
thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(DedicatedServer.LOGGER)); thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(DedicatedServer.LOGGER));
@@ -213,7 +316,13 @@ @@ -213,7 +317,13 @@
return true; return true;
} }
@@ -247,7 +248,7 @@
@Override @Override
public boolean isSpawningMonsters() { public boolean isSpawningMonsters() {
@@ -293,6 +402,7 @@ @@ -293,6 +403,7 @@
this.queryThreadGs4.stop(); this.queryThreadGs4.stop();
} }
@@ -255,7 +256,7 @@
} }
@Override @Override
@@ -302,8 +412,8 @@ @@ -302,8 +413,8 @@
} }
@Override @Override
@@ -266,7 +267,7 @@
} }
public void handleConsoleInput(String command, CommandSourceStack commandSource) { public void handleConsoleInput(String command, CommandSourceStack commandSource) {
@@ -314,7 +424,15 @@ @@ -314,7 +425,15 @@
while (!this.consoleInput.isEmpty()) { while (!this.consoleInput.isEmpty()) {
ConsoleInput servercommand = (ConsoleInput) this.consoleInput.remove(0); ConsoleInput servercommand = (ConsoleInput) this.consoleInput.remove(0);
@@ -283,7 +284,7 @@
} }
} }
@@ -383,7 +501,7 @@ @@ -383,7 +502,7 @@
@Override @Override
public boolean isUnderSpawnProtection(ServerLevel world, BlockPos pos, Player player) { public boolean isUnderSpawnProtection(ServerLevel world, BlockPos pos, Player player) {
@@ -292,7 +293,7 @@
return false; return false;
} else if (this.getPlayerList().getOps().isEmpty()) { } else if (this.getPlayerList().getOps().isEmpty()) {
return false; return false;
@@ -453,7 +571,11 @@ @@ -453,7 +572,11 @@
public boolean enforceSecureProfile() { public boolean enforceSecureProfile() {
DedicatedServerProperties dedicatedserverproperties = this.getProperties(); DedicatedServerProperties dedicatedserverproperties = this.getProperties();
@@ -305,7 +306,7 @@
} }
@Override @Override
@@ -541,16 +663,52 @@ @@ -541,16 +664,52 @@
@Override @Override
public String getPluginNames() { public String getPluginNames() {
@@ -362,7 +363,7 @@
} }
public void storeUsingWhiteList(boolean useWhitelist) { public void storeUsingWhiteList(boolean useWhitelist) {
@@ -660,4 +818,15 @@ @@ -660,4 +819,15 @@
} }
} }
} }

View File

@@ -965,6 +965,7 @@ public final class CraftServer implements Server {
@Override @Override
public void reload() { public void reload() {
org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload
this.reloadCount++; this.reloadCount++;
this.configuration = YamlConfiguration.loadConfiguration(this.getConfigFile()); this.configuration = YamlConfiguration.loadConfiguration(this.getConfigFile());
this.commandsConfiguration = YamlConfiguration.loadConfiguration(this.getCommandsConfigFile()); this.commandsConfiguration = YamlConfiguration.loadConfiguration(this.getCommandsConfigFile());
@@ -1057,6 +1058,7 @@ public final class CraftServer implements Server {
this.enablePlugins(PluginLoadOrder.POSTWORLD); this.enablePlugins(PluginLoadOrder.POSTWORLD);
if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD)); this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload
} }
@Override @Override

View File

@@ -225,7 +225,7 @@ public class SpigotConfig
SpigotConfig.restartScript = SpigotConfig.getString( "settings.restart-script", SpigotConfig.restartScript ); SpigotConfig.restartScript = SpigotConfig.getString( "settings.restart-script", SpigotConfig.restartScript );
SpigotConfig.restartMessage = SpigotConfig.transform( SpigotConfig.getString( "messages.restart", "Server is restarting" ) ); SpigotConfig.restartMessage = SpigotConfig.transform( SpigotConfig.getString( "messages.restart", "Server is restarting" ) );
SpigotConfig.commands.put( "restart", new RestartCommand( "restart" ) ); SpigotConfig.commands.put( "restart", new RestartCommand( "restart" ) );
WatchdogThread.doStart( SpigotConfig.timeoutTime, SpigotConfig.restartOnCrash ); // WatchdogThread.doStart( SpigotConfig.timeoutTime, SpigotConfig.restartOnCrash ); // Paper - moved to after paper config initialization
} }
public static boolean bungee; public static boolean bungee;

View File

@@ -14,6 +14,10 @@ public class WatchdogThread extends Thread
private static WatchdogThread instance; private static WatchdogThread instance;
private long timeoutTime; private long timeoutTime;
private boolean restart; private boolean restart;
private final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting
private final long earlyWarningDelay; // Paper
public static volatile boolean hasStarted; // Paper
private long lastEarlyWarning; // Paper - Keep track of short dump times to avoid spamming console with short dumps
private volatile long lastTick; private volatile long lastTick;
private volatile boolean stopping; private volatile boolean stopping;
@@ -22,6 +26,8 @@ public class WatchdogThread extends Thread
super( "Paper Watchdog Thread" ); super( "Paper Watchdog Thread" );
this.timeoutTime = timeoutTime; this.timeoutTime = timeoutTime;
this.restart = restart; this.restart = restart;
earlyWarningEvery = Math.min(io.papermc.paper.configuration.GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime); // Paper
earlyWarningDelay = Math.min(io.papermc.paper.configuration.GlobalConfiguration.get().watchdog.earlyWarningDelay, timeoutTime); // Paper
} }
private static long monotonicMillis() private static long monotonicMillis()
@@ -61,9 +67,18 @@ public class WatchdogThread extends Thread
while ( !this.stopping ) while ( !this.stopping )
{ {
// //
if ( this.lastTick != 0 && this.timeoutTime > 0 && WatchdogThread.monotonicMillis() > this.lastTick + this.timeoutTime && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable // Paper start
Logger log = Bukkit.getServer().getLogger();
long currentTime = WatchdogThread.monotonicMillis();
if ( this.lastTick != 0 && this.timeoutTime > 0 && currentTime > this.lastTick + this.earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable
{ {
Logger log = Bukkit.getServer().getLogger(); boolean isLongTimeout = currentTime > lastTick + timeoutTime;
// Don't spam early warning dumps
if ( !isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay)) continue;
if ( !isLongTimeout && MinecraftServer.getServer().hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
lastEarlyWarning = currentTime;
if (isLongTimeout) {
// Paper end
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper
log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" ); log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" );
@@ -92,29 +107,45 @@ public class WatchdogThread extends Thread
} }
} }
// Paper end // Paper end
} else
{
log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---");
log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
}
// Paper end - Different message for short timeout
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
// //
// Paper start - Only print full dump on long timeouts
if ( isLongTimeout )
{
log.log( Level.SEVERE, "Entire Thread Dump:" ); log.log( Level.SEVERE, "Entire Thread Dump:" );
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true ); ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true );
for ( ThreadInfo thread : threads ) for ( ThreadInfo thread : threads )
{ {
WatchdogThread.dumpThread( thread, log ); WatchdogThread.dumpThread( thread, log );
} }
} else {
log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---");
}
log.log( Level.SEVERE, "------------------------------" ); log.log( Level.SEVERE, "------------------------------" );
if ( isLongTimeout )
{
if ( this.restart && !MinecraftServer.getServer().hasStopped() ) if ( this.restart && !MinecraftServer.getServer().hasStopped() )
{ {
RestartCommand.restart(); RestartCommand.restart();
} }
break; break;
} // Paper end
} }
try try
{ {
sleep( 10000 ); sleep( 1000 ); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout
} catch ( InterruptedException ex ) } catch ( InterruptedException ex )
{ {
this.interrupt(); this.interrupt();