112 lines
4.8 KiB
Java
112 lines
4.8 KiB
Java
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.Util;
|
|
import net.minecraft.server.Bootstrap;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.util.TimeUtil;
|
|
import net.minecraft.world.level.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 p_139786_) {
|
|
this.server = p_139786_;
|
|
this.maxTickTimeNanos = p_139786_.getMaxTickLength() * TimeUtil.NANOSECONDS_PER_MILLISECOND;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
while (this.server.isRunning()) {
|
|
long i = this.server.getNextTickTime();
|
|
long j = Util.getNanos();
|
|
long k = j - i;
|
|
if (k > this.maxTickTimeNanos) {
|
|
LOGGER.error(
|
|
LogUtils.FATAL_MARKER,
|
|
"A single server tick took {} seconds (should be max {})",
|
|
String.format(Locale.ROOT, "%.2f", (float)k / (float)TimeUtil.NANOSECONDS_PER_SECOND),
|
|
String.format(Locale.ROOT, "%.2f", this.server.tickRateManager().millisecondsPerTick() / (float)TimeUtil.MILLISECONDS_PER_SECOND)
|
|
);
|
|
LOGGER.error(LogUtils.FATAL_MARKER, "Considering it to be crashed, server will forcibly shutdown.");
|
|
CrashReport crashreport = createWatchdogCrashReport("Watching Server", this.server.getRunningThread().threadId());
|
|
this.server.fillSystemReport(crashreport.getSystemReport());
|
|
CrashReportCategory crashreportcategory = crashreport.addCategory("Performance stats");
|
|
crashreportcategory.setDetail("Random tick rate", () -> this.server.getWorldData().getGameRules().getRule(GameRules.RULE_RANDOMTICKING).toString());
|
|
crashreportcategory.setDetail(
|
|
"Level stats",
|
|
() -> Streams.stream(this.server.getAllLevels())
|
|
.map(p_390132_ -> p_390132_.dimension().location() + ": " + p_390132_.getWatchdogStats())
|
|
.collect(Collectors.joining(",\n"))
|
|
);
|
|
Bootstrap.realStdoutPrintln("Crash report:\n" + crashreport.getFriendlyReport(ReportType.CRASH));
|
|
Path path = this.server.getServerDirectory().resolve("crash-reports").resolve("crash-" + Util.getFilenameFormattedDateTime() + "-server.txt");
|
|
if (crashreport.saveToFile(path, ReportType.CRASH)) {
|
|
LOGGER.error("This crash report has been saved to: {}", path.toAbsolutePath());
|
|
} else {
|
|
LOGGER.error("We were unable to save this crash report to disk.");
|
|
}
|
|
|
|
this.exit();
|
|
}
|
|
|
|
try {
|
|
Thread.sleep((i + this.maxTickTimeNanos - j) / TimeUtil.NANOSECONDS_PER_MILLISECOND);
|
|
} catch (InterruptedException interruptedexception) {
|
|
}
|
|
}
|
|
}
|
|
|
|
public static CrashReport createWatchdogCrashReport(String p_362648_, long p_368427_) {
|
|
ThreadMXBean threadmxbean = ManagementFactory.getThreadMXBean();
|
|
ThreadInfo[] athreadinfo = threadmxbean.dumpAllThreads(true, true);
|
|
StringBuilder stringbuilder = new StringBuilder();
|
|
Error error = new Error("Watchdog");
|
|
|
|
for (ThreadInfo threadinfo : athreadinfo) {
|
|
if (threadinfo.getThreadId() == p_368427_) {
|
|
error.setStackTrace(threadinfo.getStackTrace());
|
|
}
|
|
|
|
stringbuilder.append(threadinfo);
|
|
stringbuilder.append("\n");
|
|
}
|
|
|
|
CrashReport crashreport = new CrashReport(p_362648_, error);
|
|
CrashReportCategory crashreportcategory = crashreport.addCategory("Thread Dump");
|
|
crashreportcategory.setDetail("Threads", stringbuilder);
|
|
return crashreport;
|
|
}
|
|
|
|
private void exit() {
|
|
try {
|
|
Timer timer = new Timer();
|
|
timer.schedule(new TimerTask() {
|
|
@Override
|
|
public void run() {
|
|
Runtime.getRuntime().halt(1);
|
|
}
|
|
}, 10000L);
|
|
System.exit(1);
|
|
} catch (Throwable throwable) {
|
|
Runtime.getRuntime().halt(1);
|
|
}
|
|
}
|
|
} |