110 lines
4.9 KiB
Java
110 lines
4.9 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Streams
|
|
* com.mojang.logging.LogUtils
|
|
* org.slf4j.Logger
|
|
*/
|
|
package net.minecraft.server.dedicated;
|
|
|
|
import com.google.common.collect.Streams;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.lang.management.ManagementFactory;
|
|
import java.lang.management.ThreadInfo;
|
|
import java.lang.management.ThreadMXBean;
|
|
import java.nio.file.Path;
|
|
import java.util.Locale;
|
|
import java.util.Timer;
|
|
import java.util.TimerTask;
|
|
import java.util.stream.Collectors;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.ReportType;
|
|
import net.minecraft.server.Bootstrap;
|
|
import net.minecraft.server.dedicated.DedicatedServer;
|
|
import net.minecraft.util.TimeUtil;
|
|
import net.minecraft.util.Util;
|
|
import net.minecraft.world.level.gamerules.GameRules;
|
|
import org.slf4j.Logger;
|
|
|
|
public class ServerWatchdog
|
|
implements Runnable {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final long MAX_SHUTDOWN_TIME = 10000L;
|
|
private static final int SHUTDOWN_STATUS = 1;
|
|
private final DedicatedServer server;
|
|
private final long maxTickTimeNanos;
|
|
|
|
public ServerWatchdog(DedicatedServer server) {
|
|
this.server = server;
|
|
this.maxTickTimeNanos = server.getMaxTickLength() * TimeUtil.NANOSECONDS_PER_MILLISECOND;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
while (this.server.isRunning()) {
|
|
long nextTickTimeNanos = this.server.getNextTickTime();
|
|
long currentTimeNanos = Util.getNanos();
|
|
long deltaNanos = currentTimeNanos - nextTickTimeNanos;
|
|
if (deltaNanos > this.maxTickTimeNanos) {
|
|
LOGGER.error(LogUtils.FATAL_MARKER, "A single server tick took {} seconds (should be max {})", (Object)String.format(Locale.ROOT, "%.2f", Float.valueOf((float)deltaNanos / (float)TimeUtil.NANOSECONDS_PER_SECOND)), (Object)String.format(Locale.ROOT, "%.2f", Float.valueOf(this.server.tickRateManager().millisecondsPerTick() / (float)TimeUtil.MILLISECONDS_PER_SECOND)));
|
|
LOGGER.error(LogUtils.FATAL_MARKER, "Considering it to be crashed, server will forcibly shutdown.");
|
|
CrashReport report = ServerWatchdog.createWatchdogCrashReport("Watching Server", this.server.getRunningThread().threadId());
|
|
this.server.fillSystemReport(report.getSystemReport());
|
|
CrashReportCategory serverStats = report.addCategory("Performance stats");
|
|
serverStats.setDetail("Random tick rate", () -> this.server.getWorldData().getGameRules().getAsString(GameRules.RANDOM_TICK_SPEED));
|
|
serverStats.setDetail("Level stats", () -> Streams.stream(this.server.getAllLevels()).map(level -> String.valueOf(level.dimension().identifier()) + ": " + level.getWatchdogStats()).collect(Collectors.joining(",\n")));
|
|
Bootstrap.realStdoutPrintln("Crash report:\n" + report.getFriendlyReport(ReportType.CRASH));
|
|
Path file = this.server.getServerDirectory().resolve("crash-reports").resolve("crash-" + Util.getFilenameFormattedDateTime() + "-server.txt");
|
|
if (report.saveToFile(file, ReportType.CRASH)) {
|
|
LOGGER.error("This crash report has been saved to: {}", (Object)file.toAbsolutePath());
|
|
} else {
|
|
LOGGER.error("We were unable to save this crash report to disk.");
|
|
}
|
|
this.exit();
|
|
}
|
|
try {
|
|
Thread.sleep((nextTickTimeNanos + this.maxTickTimeNanos - currentTimeNanos) / TimeUtil.NANOSECONDS_PER_MILLISECOND);
|
|
}
|
|
catch (InterruptedException interruptedException) {}
|
|
}
|
|
}
|
|
|
|
public static CrashReport createWatchdogCrashReport(String message, long mainThreadId) {
|
|
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
|
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
|
|
StringBuilder builder = new StringBuilder();
|
|
Error exception = new Error("Watchdog");
|
|
for (ThreadInfo threadInfo : threadInfos) {
|
|
if (threadInfo.getThreadId() == mainThreadId) {
|
|
exception.setStackTrace(threadInfo.getStackTrace());
|
|
}
|
|
builder.append(threadInfo);
|
|
builder.append("\n");
|
|
}
|
|
CrashReport report = new CrashReport(message, exception);
|
|
CrashReportCategory threadDump = report.addCategory("Thread Dump");
|
|
threadDump.setDetail("Threads", builder);
|
|
return report;
|
|
}
|
|
|
|
private void exit() {
|
|
try {
|
|
Timer timer = new Timer();
|
|
timer.schedule(new TimerTask(this){
|
|
|
|
@Override
|
|
public void run() {
|
|
Runtime.getRuntime().halt(1);
|
|
}
|
|
}, 10000L);
|
|
System.exit(1);
|
|
}
|
|
catch (Throwable ignored) {
|
|
Runtime.getRuntime().halt(1);
|
|
}
|
|
}
|
|
}
|
|
|