From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: MrHua269 Date: Sun, 10 Mar 2024 05:04:33 +0000 Subject: [PATCH] Add a simple watchdog for tick regions diff --git a/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java b/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java index fc3d332aa1c1d469cedfe2aaa7102dcd78e25642..1ae61bc6603dd3ac290e3ead20416f9c5b63ff02 100644 --- a/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java +++ b/src/main/java/io/papermc/paper/threadedregions/RegionizedServer.java @@ -279,6 +279,7 @@ public final class RegionizedServer { */ private void globalTick(final int tickCount) { + me.earthme.luminol.utils.LuminolWatchDog.globalRegionHeartBeat(); //Luminol /* if (false) { io.papermc.paper.threadedregions.ThreadedTicketLevelPropagator.main(null); diff --git a/src/main/java/io/papermc/paper/threadedregions/ThreadedRegionizer.java b/src/main/java/io/papermc/paper/threadedregions/ThreadedRegionizer.java index 531aa50f2c84e13358e8918bb0c15ea3cd036cb5..fd0053369eb68f0fd596d8acfba4a5247ef8105a 100644 --- a/src/main/java/io/papermc/paper/threadedregions/ThreadedRegionizer.java +++ b/src/main/java/io/papermc/paper/threadedregions/ThreadedRegionizer.java @@ -167,6 +167,8 @@ public final class ThreadedRegionizer globalRegionLastKeepalive = null; + private static final Map,Long>> otherTickRegionKeepalive = Maps.newHashMap(); + private static final Executor checkTimer = CompletableFuture.delayedExecutor(50, TimeUnit.MILLISECONDS); + private static final Logger logger = MinecraftServer.LOGGER; + private static int tickCount = 0; + private static boolean runScheduleNext = true; + + public static void boot(){ + runCheck(); + } + + private static void scheduleCheck(){ + checkTimer.execute(LuminolWatchDog::runCheck); + } + + private static void runCheck(){ + try { + if (MinecraftServer.getServer().isStopped() || !runScheduleNext){ + return; + } + + tickCount++; + + final List> threadsToWarn = new ArrayList<>(); + final long currentTimeNano = System.nanoTime(); + boolean shouldStopServer = false; + + synchronized (keepaliveDataLock){ + if (globalRegionLastKeepalive != null){ + final Thread lastGlobalRegionThread = globalRegionLastKeepalive.getLeft(); + final long lastTicked = globalRegionLastKeepalive.getRight(); + + final long timeEscaped = currentTimeNano - lastTicked; + + if (timeEscaped >= WatchdogConfig.warnPeriodTicks * 50 * 1000 * 1000){ + threadsToWarn.add(Pair.of(lastGlobalRegionThread,timeEscaped)); + } + } + + for(Pair warnInfo : threadsToWarn){ + final Thread targetThread = warnInfo.getLeft(); + final long timeEscaped = warnInfo.getRight(); + + final ThreadedRegionizer.ThreadedRegion targetRegion; + + synchronized (keepaliveDataLock){ + targetRegion = otherTickRegionKeepalive.get(targetThread).getLeft(); + } + + if (tickCount % WatchdogConfig.warnPeriodTicks == 0){ + dumpSingleRegion(targetThread,targetRegion,timeEscaped); + } + + if (timeEscaped > WatchdogConfig.timeOutTicks * 50 * 1000 * 1000){ + shouldStopServer = true; + runScheduleNext = false; + } + } + + if (shouldStopServer){ + MinecraftServer.getServer().stopServer(); + } + } + }finally { + if (runScheduleNext){ + scheduleCheck(); + } + } + } + + private static void dumpSingleRegion(@NotNull Thread thread, @Nullable ThreadedRegionizer.ThreadedRegion region,long timeEscaped){ + final StringBuilder messageBuilder = new StringBuilder(); + messageBuilder.append("====================================").append("\n"); + messageBuilder.append("There is a single region that has no responding in " + TimeUnit.NANOSECONDS.toSeconds(timeEscaped) + " seconds!").append("\n"); + messageBuilder.append("If you believe this is a bug,please send it to github issue after checking your plugins!").append("\n"); + messageBuilder.append("Tick Region: ").append(region == null ? "Global Region" : "ChunkX(Center): " + region.getCenterChunk().x + " ChunkZ(Center): " + region.getCenterChunk().z).append("\n"); + messageBuilder.append("Thread Name: ").append(thread.getName()).append("\n"); + messageBuilder.append("Thread Id: ").append(thread.getId()).append("\n"); + + final ThreadInfo targetThreadInfo = ManagementFactory.getThreadMXBean().getThreadInfo(thread.getId()); + + messageBuilder.append("Is Suspend: ").append(targetThreadInfo.isSuspended()).append("\n"); + messageBuilder.append("Thread State: ").append(targetThreadInfo.getThreadState()).append("\n"); + messageBuilder.append("Thread Priority: ").append(targetThreadInfo.getPriority()).append("\n"); + messageBuilder.append("Thread Stack Trace:").append("\n"); + + for (StackTraceElement stackTraceElement : StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace())){ + messageBuilder.append("\t\t").append(stackTraceElement).append("\n"); + } + + messageBuilder.append("\n"); + messageBuilder.append("===================================="); + + logger.error(messageBuilder.toString()); + } + + public static void globalRegionHeartBeat(){ + if (!WatchdogConfig.enabled){ + return; + } + + synchronized (keepaliveDataLock){ + globalRegionLastKeepalive = Pair.of(Thread.currentThread(),System.nanoTime()); + } + } + + public static void tickRegionHeartBeat(){ + if (!WatchdogConfig.enabled){ + return; + } + + final ThreadedRegionizer.ThreadedRegion currentRegion = TickRegionScheduler.getCurrentRegion(); + + if (currentRegion == null) { + return; + } + + synchronized (keepaliveDataLock){ + if (!otherTickRegionKeepalive.containsKey(Thread.currentThread())){ + otherTickRegionKeepalive.put(Thread.currentThread(),Pair.of(currentRegion,System.nanoTime())); + return; + } + + otherTickRegionKeepalive.replace(Thread.currentThread(),Pair.of(currentRegion,System.nanoTime())); + } + } + + public static void onRegionDeath(ThreadedRegionizer.ThreadedRegion currentRegion){ + if (!WatchdogConfig.enabled){ + return; + } + + synchronized (keepaliveDataLock){ + otherTickRegionKeepalive.remove(Thread.currentThread()); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 0d756dd5181a2444175184227bef648d07c17236..59b1da130e519cbe06ed5eedf9e4dc881e45ba36 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1222,6 +1222,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop