From e02d52ce8d45550a4d77f11971e31cf0732e5f0c Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 4 Feb 2020 00:49:40 +0000 Subject: Monitor average tick durations & report them in /spark tps --- .../me/lucko/spark/bukkit/BukkitSparkPlugin.java | 17 +- .../me/lucko/spark/bukkit/BukkitTickCounter.java | 51 ------ .../java/me/lucko/spark/bukkit/BukkitTickHook.java | 51 ++++++ .../me/lucko/spark/bukkit/PaperTickCounter.java | 53 ------ .../java/me/lucko/spark/bukkit/PaperTickHook.java | 53 ++++++ .../me/lucko/spark/bukkit/PaperTickReporter.java | 53 ++++++ .../placeholder/SparkPlaceholderProvider.java | 54 ++++-- .../java/me/lucko/spark/common/SparkPlatform.java | 44 +++-- .../java/me/lucko/spark/common/SparkPlugin.java | 20 ++- .../spark/common/command/modules/HealthModule.java | 81 +++++++-- .../common/command/modules/SamplerModule.java | 10 +- .../command/modules/TickMonitoringModule.java | 22 +-- .../spark/common/monitor/tick/TickMonitor.java | 16 +- .../spark/common/monitor/tick/TickStatistics.java | 187 +++++++++++++++++++++ .../spark/common/monitor/tick/TpsCalculator.java | 149 ---------------- .../spark/common/sampler/AbstractTickCounter.java | 53 ------ .../me/lucko/spark/common/sampler/Sampler.java | 5 +- .../lucko/spark/common/sampler/SamplerBuilder.java | 12 +- .../me/lucko/spark/common/sampler/TickCounter.java | 64 ------- .../sampler/aggregator/TickedDataAggregator.java | 10 +- .../common/sampler/tick/AbstractTickHook.java | 53 ++++++ .../common/sampler/tick/AbstractTickReporter.java | 45 +++++ .../lucko/spark/common/sampler/tick/TickHook.java | 64 +++++++ .../spark/common/sampler/tick/TickReporter.java | 57 +++++++ .../me/lucko/spark/common/util/RollingAverage.java | 42 ++++- .../me/lucko/spark/fabric/FabricTickCounter.java | 66 -------- .../java/me/lucko/spark/fabric/FabricTickHook.java | 66 ++++++++ .../fabric/plugin/FabricClientSparkPlugin.java | 8 +- .../fabric/plugin/FabricServerSparkPlugin.java | 8 +- .../me/lucko/spark/forge/ForgeTickCounter.java | 59 ------- .../java/me/lucko/spark/forge/ForgeTickHook.java | 59 +++++++ .../me/lucko/spark/forge/ForgeTickReporter.java | 71 ++++++++ .../spark/forge/plugin/ForgeClientSparkPlugin.java | 15 +- .../spark/forge/plugin/ForgeServerSparkPlugin.java | 15 +- .../me/lucko/spark/sponge/SpongeSparkPlugin.java | 9 +- .../me/lucko/spark/sponge/SpongeTickCounter.java | 50 ------ .../java/me/lucko/spark/sponge/SpongeTickHook.java | 50 ++++++ 37 files changed, 1080 insertions(+), 662 deletions(-) delete mode 100644 spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickCounter.java create mode 100644 spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickHook.java delete mode 100644 spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickCounter.java create mode 100644 spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickHook.java create mode 100644 spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickReporter.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/AbstractTickCounter.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/TickCounter.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickHook.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickHook.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java delete mode 100644 spark-fabric/src/main/java/me/lucko/spark/fabric/FabricTickCounter.java create mode 100644 spark-fabric/src/main/java/me/lucko/spark/fabric/FabricTickHook.java delete mode 100644 spark-forge/src/main/java/me/lucko/spark/forge/ForgeTickCounter.java create mode 100644 spark-forge/src/main/java/me/lucko/spark/forge/ForgeTickHook.java create mode 100644 spark-forge/src/main/java/me/lucko/spark/forge/ForgeTickReporter.java delete mode 100644 spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeTickCounter.java create mode 100644 spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeTickHook.java diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitSparkPlugin.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitSparkPlugin.java index 0eca719..eca4619 100644 --- a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitSparkPlugin.java +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitSparkPlugin.java @@ -25,7 +25,8 @@ import me.lucko.spark.bukkit.placeholder.SparkPlaceholderApi; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.SparkPlugin; import me.lucko.spark.common.sampler.ThreadDumper; -import me.lucko.spark.common.sampler.TickCounter; +import me.lucko.spark.common.sampler.tick.TickHook; +import me.lucko.spark.common.sampler.tick.TickReporter; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -129,16 +130,24 @@ public class BukkitSparkPlugin extends JavaPlugin implements SparkPlugin { } @Override - public TickCounter createTickCounter() { + public TickHook createTickHook() { if (classExists("com.destroystokyo.paper.event.server.ServerTickStartEvent")) { getLogger().info("Using Paper ServerTickStartEvent for tick monitoring"); - return new PaperTickCounter(this); + return new PaperTickHook(this); } else { getLogger().info("Using Bukkit scheduler for tick monitoring"); - return new BukkitTickCounter(this); + return new BukkitTickHook(this); } } + @Override + public TickReporter createTickReporter() { + if (classExists("com.destroystokyo.paper.event.server.ServerTickStartEvent")) { + return new PaperTickReporter(this); + } + return null; + } + private static boolean classExists(String className) { try { Class.forName(className); diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickCounter.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickCounter.java deleted file mode 100644 index 2a71257..0000000 --- a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickCounter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package me.lucko.spark.bukkit; - -import me.lucko.spark.common.sampler.AbstractTickCounter; -import me.lucko.spark.common.sampler.TickCounter; -import org.bukkit.plugin.Plugin; -import org.bukkit.scheduler.BukkitTask; - -public class BukkitTickCounter extends AbstractTickCounter implements TickCounter, Runnable { - private final Plugin plugin; - private BukkitTask task; - - public BukkitTickCounter(Plugin plugin) { - this.plugin = plugin; - } - - @Override - public void run() { - onTick(); - } - - @Override - public void start() { - this.task = this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, this, 1, 1); - } - - @Override - public void close() { - this.task.cancel(); - } - -} diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickHook.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickHook.java new file mode 100644 index 0000000..184656e --- /dev/null +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitTickHook.java @@ -0,0 +1,51 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package me.lucko.spark.bukkit; + +import me.lucko.spark.common.sampler.tick.AbstractTickHook; +import me.lucko.spark.common.sampler.tick.TickHook; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +public class BukkitTickHook extends AbstractTickHook implements TickHook, Runnable { + private final Plugin plugin; + private BukkitTask task; + + public BukkitTickHook(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void run() { + onTick(); + } + + @Override + public void start() { + this.task = this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, this, 1, 1); + } + + @Override + public void close() { + this.task.cancel(); + } + +} diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickCounter.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickCounter.java deleted file mode 100644 index 7189429..0000000 --- a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickCounter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package me.lucko.spark.bukkit; - -import com.destroystokyo.paper.event.server.ServerTickStartEvent; -import me.lucko.spark.common.sampler.AbstractTickCounter; -import me.lucko.spark.common.sampler.TickCounter; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.plugin.Plugin; - -public class PaperTickCounter extends AbstractTickCounter implements TickCounter, Listener { - private final Plugin plugin; - - public PaperTickCounter(Plugin plugin) { - this.plugin = plugin; - } - - @EventHandler - public void onServerTickEvent(ServerTickStartEvent e) { - onTick(); - } - - @Override - public void start() { - this.plugin.getServer().getPluginManager().registerEvents(this, this.plugin); - } - - @Override - public void close() { - HandlerList.unregisterAll(this); - } - -} diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickHook.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickHook.java new file mode 100644 index 0000000..5ad7b57 --- /dev/null +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickHook.java @@ -0,0 +1,53 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package me.lucko.spark.bukkit; + +import com.destroystokyo.paper.event.server.ServerTickStartEvent; +import me.lucko.spark.common.sampler.tick.AbstractTickHook; +import me.lucko.spark.common.sampler.tick.TickHook; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +public class PaperTickHook extends AbstractTickHook implements TickHook, Listener { + private final Plugin plugin; + + public PaperTickHook(Plugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onServerTickEvent(ServerTickStartEvent e) { + onTick(); + } + + @Override + public void start() { + this.plugin.getServer().getPluginManager().registerEvents(this, this.plugin); + } + + @Override + public void close() { + HandlerList.unregisterAll(this); + } + +} diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickReporter.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickReporter.java new file mode 100644 index 0000000..a51be66 --- /dev/null +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/PaperTickReporter.java @@ -0,0 +1,53 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package me.lucko.spark.bukkit; + +import com.destroystokyo.paper.event.server.ServerTickEndEvent; +import me.lucko.spark.common.sampler.tick.AbstractTickReporter; +import me.lucko.spark.common.sampler.tick.TickReporter; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +public class PaperTickReporter extends AbstractTickReporter implements TickReporter, Listener { + private final Plugin plugin; + + public PaperTickReporter(Plugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onServerTickEvent(ServerTickEndEvent e) { + onTick(e.getTickDuration()); + } + + @Override + public void start() { + this.plugin.getServer().getPluginManager().registerEvents(this, this.plugin); + } + + @Override + public void close() { + HandlerList.unregisterAll(this); + } + +} diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/placeholder/SparkPlaceholderProvider.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/placeholder/SparkPlaceholderProvider.java index d94ce95..0f57149 100644 --- a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/placeholder/SparkPlaceholderProvider.java +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/placeholder/SparkPlaceholderProvider.java @@ -23,7 +23,7 @@ package me.lucko.spark.bukkit.placeholder; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.command.modules.HealthModule; import me.lucko.spark.common.monitor.cpu.CpuMonitor; -import me.lucko.spark.common.monitor.tick.TpsCalculator; +import me.lucko.spark.common.monitor.tick.TickStatistics; import net.kyori.text.TextComponent; import net.kyori.text.serializer.legacy.LegacyComponentSerializer; @@ -32,37 +32,59 @@ enum SparkPlaceholderProvider { public static TextComponent respondComponent(SparkPlatform platform, String placeholder) { if (placeholder.startsWith("tps")) { - TpsCalculator tpsCalculator = platform.getTpsCalculator(); - if (tpsCalculator == null) { + TickStatistics tickStatistics = platform.getTickStatistics(); + if (tickStatistics == null) { return null; } switch (placeholder) { case "tps": - return TextComponent.builder(" ") - .append(HealthModule.formatTps(tpsCalculator.avg5Sec())).append(TextComponent.of(", ")) - .append(HealthModule.formatTps(tpsCalculator.avg10Sec())).append(TextComponent.of(", ")) - .append(HealthModule.formatTps(tpsCalculator.avg1Min())).append(TextComponent.of(", ")) - .append(HealthModule.formatTps(tpsCalculator.avg5Min())).append(TextComponent.of(", ")) - .append(HealthModule.formatTps(tpsCalculator.avg15Min())) + return TextComponent.builder("") + .append(HealthModule.formatTps(tickStatistics.tps5Sec())).append(TextComponent.of(", ")) + .append(HealthModule.formatTps(tickStatistics.tps10Sec())).append(TextComponent.of(", ")) + .append(HealthModule.formatTps(tickStatistics.tps1Min())).append(TextComponent.of(", ")) + .append(HealthModule.formatTps(tickStatistics.tps5Min())).append(TextComponent.of(", ")) + .append(HealthModule.formatTps(tickStatistics.tps15Min())) .build(); case "tps_5s": - return HealthModule.formatTps(tpsCalculator.avg5Sec()); + return HealthModule.formatTps(tickStatistics.tps5Sec()); case "tps_10s": - return HealthModule.formatTps(tpsCalculator.avg10Sec()); + return HealthModule.formatTps(tickStatistics.tps10Sec()); case "tps_1m": - return HealthModule.formatTps(tpsCalculator.avg1Min()); + return HealthModule.formatTps(tickStatistics.tps1Min()); case "tps_5m": - return HealthModule.formatTps(tpsCalculator.avg5Min()); + return HealthModule.formatTps(tickStatistics.tps5Min()); case "tps_15m": - return HealthModule.formatTps(tpsCalculator.avg15Min()); + return HealthModule.formatTps(tickStatistics.tps15Min()); + } + } + + if (placeholder.startsWith("tickduration")) { + TickStatistics tickStatistics = platform.getTickStatistics(); + if (tickStatistics == null || !tickStatistics.isDurationSupported()) { + return null; + } + + switch (placeholder) { + case "tickduration": + return TextComponent.builder("") + .append(HealthModule.formatTickDurations(tickStatistics.duration5Sec())).append(TextComponent.of(", ")) + .append(HealthModule.formatTickDurations(tickStatistics.duration10Sec())).append(TextComponent.of(", ")) + .append(HealthModule.formatTickDurations(tickStatistics.duration1Min())) + .build(); + case "tickduration_5s": + return HealthModule.formatTickDurations(tickStatistics.duration5Sec()); + case "tickduration_10s": + return HealthModule.formatTickDurations(tickStatistics.duration10Sec()); + case "tickduration_1m": + return HealthModule.formatTickDurations(tickStatistics.duration1Min()); } } if (placeholder.startsWith("cpu")) { switch (placeholder) { case "cpu_system": - return TextComponent.builder(" ") + return TextComponent.builder("") .append(HealthModule.formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(TextComponent.of(", ")) .append(HealthModule.formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(TextComponent.of(", ")) .append(HealthModule.formatCpuUsage(CpuMonitor.systemLoad15MinAvg())) @@ -74,7 +96,7 @@ enum SparkPlaceholderProvider { case "cpu_system_15m": return HealthModule.formatCpuUsage(CpuMonitor.systemLoad15MinAvg()); case "cpu_process": - return TextComponent.builder(" ") + return TextComponent.builder("") .append(HealthModule.formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(TextComponent.of(", ")) .append(HealthModule.formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(TextComponent.of(", ")) .append(HealthModule.formatCpuUsage(CpuMonitor.processLoad15MinAvg())) diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java index dc88306..340fa34 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java +++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java @@ -35,8 +35,9 @@ import me.lucko.spark.common.command.sender.CommandSender; import me.lucko.spark.common.command.tabcomplete.CompletionSupplier; import me.lucko.spark.common.command.tabcomplete.TabCompleter; import me.lucko.spark.common.monitor.cpu.CpuMonitor; -import me.lucko.spark.common.monitor.tick.TpsCalculator; -import me.lucko.spark.common.sampler.TickCounter; +import me.lucko.spark.common.monitor.tick.TickStatistics; +import me.lucko.spark.common.sampler.tick.TickHook; +import me.lucko.spark.common.sampler.tick.TickReporter; import me.lucko.spark.common.util.BytebinClient; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; @@ -66,8 +67,9 @@ public class SparkPlatform { private final List commandModules; private final List commands; private final ActivityLog activityLog; - private final TickCounter tickCounter; - private final TpsCalculator tpsCalculator; + private final TickHook tickHook; + private final TickReporter tickReporter; + private final TickStatistics tickStatistics; public SparkPlatform(SparkPlugin plugin) { this.plugin = plugin; @@ -89,21 +91,29 @@ public class SparkPlatform { this.activityLog = new ActivityLog(plugin.getPluginDirectory().resolve("activity.json")); this.activityLog.load(); - this.tickCounter = plugin.createTickCounter(); - this.tpsCalculator = this.tickCounter != null ? new TpsCalculator() : null; + this.tickHook = plugin.createTickHook(); + this.tickReporter = plugin.createTickReporter(); + this.tickStatistics = this.tickHook != null ? new TickStatistics() : null; } public void enable() { - if (this.tickCounter != null) { - this.tickCounter.addTickTask(this.tpsCalculator); - this.tickCounter.start(); + if (this.tickHook != null) { + this.tickHook.addCallback(this.tickStatistics); + this.tickHook.start(); + } + if (this.tickReporter != null) { + this.tickReporter.addCallback(this.tickStatistics); + this.tickReporter.start(); } CpuMonitor.ensureMonitoring(); } public void disable() { - if (this.tickCounter != null) { - this.tickCounter.close(); + if (this.tickHook != null) { + this.tickHook.close(); + } + if (this.tickReporter != null) { + this.tickReporter.close(); } for (CommandModule module : this.commandModules) { @@ -119,12 +129,16 @@ public class SparkPlatform { return this.activityLog; } - public TickCounter getTickCounter() { - return this.tickCounter; + public TickHook getTickHook() { + return this.tickHook; + } + + public TickReporter getTickReporter() { + return this.tickReporter; } - public TpsCalculator getTpsCalculator() { - return this.tpsCalculator; + public TickStatistics getTickStatistics() { + return this.tickStatistics; } public void executeCommand(CommandSender sender, String[] args) { diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java index 1171b33..c0a928d 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java +++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java @@ -22,7 +22,8 @@ package me.lucko.spark.common; import me.lucko.spark.common.command.sender.CommandSender; import me.lucko.spark.common.sampler.ThreadDumper; -import me.lucko.spark.common.sampler.TickCounter; +import me.lucko.spark.common.sampler.tick.TickHook; +import me.lucko.spark.common.sampler.tick.TickReporter; import java.nio.file.Path; import java.util.stream.Stream; @@ -79,13 +80,24 @@ public interface SparkPlugin { } /** - * Creates a tick counter for the platform, if supported. + * Creates a tick hook for the platform, if supported. * *

Returns {@code null} if the platform does not have "ticks"

* - * @return a new tick counter + * @return a new tick hook */ - default TickCounter createTickCounter() { + default TickHook createTickHook() { + return null; + } + + /** + * Creates a tick reporter for the platform, if supported. + * + *

Returns {@code null} if the platform does not have "ticks"

+ * + * @return a new tick reporter + */ + default TickReporter createTickReporter() { return null; } diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java index 0737a00..08233db 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java @@ -25,8 +25,9 @@ import me.lucko.spark.common.command.Command; import me.lucko.spark.common.command.CommandModule; import me.lucko.spark.common.command.tabcomplete.TabCompleter; import me.lucko.spark.common.monitor.cpu.CpuMonitor; -import me.lucko.spark.common.monitor.tick.TpsCalculator; +import me.lucko.spark.common.monitor.tick.TickStatistics; import me.lucko.spark.common.util.FormatUtil; +import me.lucko.spark.common.util.RollingAverage; import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; @@ -52,18 +53,29 @@ public class HealthModule implements CommandModule { consumer.accept(Command.builder() .aliases("tps", "cpu") .executor((platform, sender, resp, arguments) -> { - TpsCalculator tpsCalculator = platform.getTpsCalculator(); - if (tpsCalculator != null) { + TickStatistics tickStatistics = platform.getTickStatistics(); + if (tickStatistics != null) { resp.replyPrefixed(TextComponent.of("TPS from last 5s, 10s, 1m, 5m, 15m:")); resp.replyPrefixed(TextComponent.builder(" ") - .append(formatTps(tpsCalculator.avg5Sec())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg10Sec())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg1Min())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg5Min())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg15Min())) + .append(formatTps(tickStatistics.tps5Sec())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps10Sec())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps1Min())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps5Min())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps15Min())) .build() ); resp.replyPrefixed(TextComponent.empty()); + + if (tickStatistics.isDurationSupported()) { + resp.replyPrefixed(TextComponent.of("Tick durations (avg/min/max ms) from last 5s, 10s, 1m:")); + resp.replyPrefixed(TextComponent.builder(" ") + .append(formatTickDurations(tickStatistics.duration5Sec())).append(TextComponent.of(", ")) + .append(formatTickDurations(tickStatistics.duration10Sec())).append(TextComponent.of(", ")) + .append(formatTickDurations(tickStatistics.duration1Min())) + .build() + ); + resp.replyPrefixed(TextComponent.empty()); + } } resp.replyPrefixed(TextComponent.of("CPU usage from last 10s, 1m, 15m:")); @@ -95,8 +107,8 @@ public class HealthModule implements CommandModule { List report = new LinkedList<>(); report.add(TextComponent.empty()); - TpsCalculator tpsCalculator = platform.getTpsCalculator(); - if (tpsCalculator != null) { + TickStatistics tickStatistics = platform.getTickStatistics(); + if (tickStatistics != null) { report.add(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) .append(TextComponent.space()) @@ -104,14 +116,30 @@ public class HealthModule implements CommandModule { .build() ); report.add(TextComponent.builder(" ") - .append(formatTps(tpsCalculator.avg5Sec())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg10Sec())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg1Min())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg5Min())).append(TextComponent.of(", ")) - .append(formatTps(tpsCalculator.avg15Min())) + .append(formatTps(tickStatistics.tps5Sec())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps10Sec())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps1Min())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps5Min())).append(TextComponent.of(", ")) + .append(formatTps(tickStatistics.tps15Min())) .build() ); report.add(TextComponent.empty()); + + if (tickStatistics.isDurationSupported()) { + report.add(TextComponent.builder("") + .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) + .append(TextComponent.space()) + .append(TextComponent.of("Tick durations (avg/min/max ms) from last 5s, 10s, 1m:", TextColor.GOLD)) + .build() + ); + report.add(TextComponent.builder(" ") + .append(formatTickDurations(tickStatistics.duration5Sec())).append(TextComponent.of(", ")) + .append(formatTickDurations(tickStatistics.duration10Sec())).append(TextComponent.of(", ")) + .append(formatTickDurations(tickStatistics.duration1Min())) + .build() + ); + report.add(TextComponent.empty()); + } } report.add(TextComponent.builder("") @@ -269,6 +297,29 @@ public class HealthModule implements CommandModule { return TextComponent.of( (tps > 20.0 ? "*" : "") + Math.min(Math.round(tps * 100.0) / 100.0, 20.0), color); } + public static TextComponent formatTickDurations(RollingAverage average){ + return TextComponent.builder("") + .append(formatTickDuration(average.getAverage())) + .append(TextComponent.of('/', TextColor.GRAY)) + .append(formatTickDuration(average.getMin())) + .append(TextComponent.of('/', TextColor.GRAY)) + .append(formatTickDuration(average.getMax())) + .build(); + } + + public static TextComponent formatTickDuration(double duration){ + TextColor color; + if (duration >= 50d) { + color = TextColor.RED; + } else if (duration >= 40d) { + color = TextColor.YELLOW; + } else { + color = TextColor.GREEN; + } + + return TextComponent.of(String.format("%.1f", duration), color); + } + public static TextComponent formatCpuUsage(double usage) { TextColor color; if (usage > 0.9) { diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java index cd98aa4..919931e 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java @@ -32,7 +32,7 @@ import me.lucko.spark.common.sampler.SamplerBuilder; import me.lucko.spark.common.sampler.ThreadDumper; import me.lucko.spark.common.sampler.ThreadGrouper; import me.lucko.spark.common.sampler.ThreadNodeOrder; -import me.lucko.spark.common.sampler.TickCounter; +import me.lucko.spark.common.sampler.tick.TickHook; import net.kyori.text.TextComponent; import net.kyori.text.event.ClickEvent; import net.kyori.text.format.TextColor; @@ -167,10 +167,10 @@ public class SamplerModule implements CommandModule { } int ticksOver = arguments.intFlag("only-ticks-over"); - TickCounter tickCounter = null; + TickHook tickHook = null; if (ticksOver != -1) { - tickCounter = platform.getTickCounter(); - if (tickCounter == null) { + tickHook = platform.getTickHook(); + if (tickHook == null) { resp.replyPrefixed(TextComponent.of("Tick counting is not supported!", TextColor.RED)); return; } @@ -193,7 +193,7 @@ public class SamplerModule implements CommandModule { builder.includeLineNumbers(includeLineNumbers); builder.ignoreSleeping(ignoreSleeping); if (ticksOver != -1) { - builder.ticksOver(ticksOver, tickCounter); + builder.ticksOver(ticksOver, tickHook); } Sampler sampler = this.activeSampler = builder.start(); diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java index 0ebb252..245a55b 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java @@ -26,7 +26,7 @@ import me.lucko.spark.common.command.CommandModule; import me.lucko.spark.common.command.CommandResponseHandler; import me.lucko.spark.common.command.tabcomplete.TabCompleter; import me.lucko.spark.common.monitor.tick.TickMonitor; -import me.lucko.spark.common.sampler.TickCounter; +import me.lucko.spark.common.sampler.tick.TickHook; import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; @@ -35,14 +35,14 @@ import java.util.function.Consumer; public class TickMonitoringModule implements CommandModule { - /** The tick monitor instance currently running, if any */ - private TickCounter tickCounter = null; + /** The tick hook instance currently running, if any */ + private TickHook tickHook = null; private ReportingTickMonitor activeTickMonitor = null; @Override public void close() { if (this.activeTickMonitor != null) { - this.tickCounter.removeTickTask(this.activeTickMonitor); + this.tickHook.removeCallback(this.activeTickMonitor); this.activeTickMonitor.close(); this.activeTickMonitor = null; } @@ -55,10 +55,10 @@ public class TickMonitoringModule implements CommandModule { .argumentUsage("threshold", "percentage increase") .argumentUsage("without-gc", null) .executor((platform, sender, resp, arguments) -> { - if (this.tickCounter == null) { - this.tickCounter = platform.getTickCounter(); + if (this.tickHook == null) { + this.tickHook = platform.getTickHook(); } - if (this.tickCounter == null) { + if (this.tickHook == null) { resp.replyPrefixed(TextComponent.of("Not supported!", TextColor.RED)); return; } @@ -69,8 +69,8 @@ public class TickMonitoringModule implements CommandModule { threshold = 100; } - this.activeTickMonitor = new ReportingTickMonitor(platform, resp, this.tickCounter, threshold, !arguments.boolFlag("without-gc")); - this.tickCounter.addTickTask(this.activeTickMonitor); + this.activeTickMonitor = new ReportingTickMonitor(platform, resp, this.tickHook, threshold, !arguments.boolFlag("without-gc")); + this.tickHook.addCallback(this.activeTickMonitor); } else { close(); resp.broadcastPrefixed(TextComponent.of("Tick monitor disabled.")); @@ -84,8 +84,8 @@ public class TickMonitoringModule implements CommandModule { private static class ReportingTickMonitor extends TickMonitor { private final CommandResponseHandler resp; - ReportingTickMonitor(SparkPlatform platform, CommandResponseHandler resp, TickCounter tickCounter, int percentageChangeThreshold, boolean monitorGc) { - super(platform, tickCounter, percentageChangeThreshold, monitorGc); + ReportingTickMonitor(SparkPlatform platform, CommandResponseHandler resp, TickHook tickHook, int percentageChangeThreshold, boolean monitorGc) { + super(platform, tickHook, percentageChangeThreshold, monitorGc); this.resp = resp; } diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java index fa4b83c..7015b4a 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java +++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java @@ -23,7 +23,7 @@ package me.lucko.spark.common.monitor.tick; import com.sun.management.GarbageCollectionNotificationInfo; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.monitor.memory.GarbageCollectionMonitor; -import me.lucko.spark.common.sampler.TickCounter; +import me.lucko.spark.common.sampler.tick.TickHook; import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; @@ -31,11 +31,11 @@ import net.kyori.text.format.TextColor; import java.text.DecimalFormat; import java.util.DoubleSummaryStatistics; -public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollectionMonitor.Listener, AutoCloseable { +public abstract class TickMonitor implements TickHook.Callback, GarbageCollectionMonitor.Listener, AutoCloseable { private static final DecimalFormat df = new DecimalFormat("#.##"); private final SparkPlatform platform; - private final TickCounter tickCounter; + private final TickHook tickHook; private final int zeroTick; private final GarbageCollectionMonitor garbageCollectionMonitor; private final int percentageChangeThreshold; @@ -46,10 +46,10 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec private final DoubleSummaryStatistics averageTickTime = new DoubleSummaryStatistics(); private double avg; - public TickMonitor(SparkPlatform platform, TickCounter tickCounter, int percentageChangeThreshold, boolean monitorGc) { + public TickMonitor(SparkPlatform platform, TickHook tickHook, int percentageChangeThreshold, boolean monitorGc) { this.platform = platform; - this.tickCounter = tickCounter; - this.zeroTick = tickCounter.getCurrentTick(); + this.tickHook = tickHook; + this.zeroTick = tickHook.getCurrentTick(); this.percentageChangeThreshold = percentageChangeThreshold; if (monitorGc) { @@ -61,7 +61,7 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec } public int getCurrentTick() { - return this.tickCounter.getCurrentTick() - this.zeroTick; + return this.tickHook.getCurrentTick() - this.zeroTick; } protected abstract void sendMessage(Component message); @@ -74,7 +74,7 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec } @Override - public void onTick(TickCounter counter) { + public void onTick(TickHook hook) { double now = ((double) System.nanoTime()) / 1000000d; // init diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java new file mode 100644 index 0000000..c91ceae --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java @@ -0,0 +1,187 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package me.lucko.spark.common.monitor.tick; + +import me.lucko.spark.common.sampler.tick.TickHook; +import me.lucko.spark.common.sampler.tick.TickReporter; +import me.lucko.spark.common.util.RollingAverage; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.concurrent.TimeUnit; + +/** + * Calculates the servers TPS (ticks per second) rate. + * + *

The code use to calculate the TPS is the same as the code used by the Minecraft server itself. + * This means that this class will output values the same as the /tps command.

+ * + *

We calculate our own values instead of pulling them from the server for two reasons. Firstly, + * it's easier - pulling from the server requires reflection code on each of the platforms, we'd + * rather avoid that. Secondly, it allows us to generate rolling averages over a shorter period of + * time.

+ */ +public class TickStatistics implements TickHook.Callback, TickReporter.Callback { + + private static final long SEC_IN_NANO = TimeUnit.SECONDS.toNanos(1); + private static final int TPS = 20; + private static final int TPS_SAMPLE_INTERVAL = 20; + private static final BigDecimal TPS_BASE = new BigDecimal(SEC_IN_NANO).multiply(new BigDecimal(TPS_SAMPLE_INTERVAL)); + + private final TpsRollingAverage tps5Sec = new TpsRollingAverage(5); + private final TpsRollingAverage tps10Sec = new TpsRollingAverage(10); + private final TpsRollingAverage tps1Min = new TpsRollingAverage(60); + private final TpsRollingAverage tps5Min = new TpsRollingAverage(60 * 5); + private final TpsRollingAverage tps15Min = new TpsRollingAverage(60 * 15); + private final TpsRollingAverage[] tpsAverages = {this.tps5Sec, this.tps10Sec, this.tps1Min, this.tps5Min, this.tps15Min}; + + private boolean durationSupported = false; + private final RollingAverage tickDuration5Sec = new RollingAverage(TPS * 5); + private final RollingAverage tickDuration10Sec = new RollingAverage(TPS * 10); + private final RollingAverage tickDuration1Min = new RollingAverage(TPS * 60); + private final RollingAverage[] tickDurationAverages = {this.tickDuration5Sec, this.tickDuration10Sec, this.tickDuration1Min}; + + private long last = 0; + + public boolean isDurationSupported() { + return this.durationSupported; + } + + @Override + public void onTick(TickHook hook) { + if (hook.getCurrentTick() % TPS_SAMPLE_INTERVAL != 0) { + return; + } + + long now = System.nanoTime(); + + if (this.last == 0) { + this.last = now; + return; + } + + long diff = now - this.last; + BigDecimal currentTps = TPS_BASE.divide(new BigDecimal(diff), 30, RoundingMode.HALF_UP); + BigDecimal total = currentTps.multiply(new BigDecimal(diff)); + + for (TpsRollingAverage rollingAverage : this.tpsAverages) { + rollingAverage.add(currentTps, diff, total); + } + + this.last = now; + } + + @Override + public void onTick(double duration) { + this.durationSupported = true; + BigDecimal decimal = BigDecimal.valueOf(duration); + for (RollingAverage rollingAverage : this.tickDurationAverages) { + rollingAverage.add(decimal); + } + } + + public double tps5Sec() { + return this.tps5Sec.getAverage(); + } + + public double tps10Sec() { + return this.tps10Sec.getAverage(); + } + + public double tps1Min() { + return this.tps1Min.getAverage(); + } + + public double tps5Min() { + return this.tps5Min.getAverage(); + } + + public double tps15Min() { + return this.tps15Min.getAverage(); + } + + public RollingAverage duration5Sec() { + if (!this.durationSupported) { + return null; + } + return this.tickDuration5Sec; + } + + public RollingAverage duration10Sec() { + if (!this.durationSupported) { + return null; + } + return this.tickDuration10Sec; + } + + public RollingAverage duration1Min() { + if (!this.durationSupported) { + return null; + } + return this.tickDuration1Min; + } + + + /** + * Rolling average calculator taken. + * + *

This code is taken from PaperMC/Paper, licensed under MIT.

+ * + * @author aikar (PaperMC) https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0021-Further-improve-server-tick-loop.patch + */ + public static final class TpsRollingAverage { + private final int size; + private long time; + private BigDecimal total; + private int index = 0; + private final BigDecimal[] samples; + private final long[] times; + + TpsRollingAverage(int size) { + this.size = size; + this.time = size * SEC_IN_NANO; + this.total = new BigDecimal(TPS).multiply(new BigDecimal(SEC_IN_NANO)).multiply(new BigDecimal(size)); + this.samples = new BigDecimal[size]; + this.times = new long[size]; + for (int i = 0; i < size; i++) { + this.samples[i] = new BigDecimal(TPS); + this.times[i] = SEC_IN_NANO; + } + } + + public void add(BigDecimal x, long t, BigDecimal total) { + this.time -= this.times[this.index]; + this.total = this.total.subtract(this.samples[this.index].multiply(new BigDecimal(this.times[this.index]))); + this.samples[this.index] = x; + this.times[this.index] = t; + this.time += t; + this.total = this.total.add(total); + if (++this.index == this.size) { + this.index = 0; + } + } + + public double getAverage() { + return this.total.divide(new BigDecimal(this.time), 30, RoundingMode.HALF_UP).doubleValue(); + } + } + +} diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java deleted file mode 100644 index 6b4f2d1..0000000 --- a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package me.lucko.spark.common.monitor.tick; - -import me.lucko.spark.common.sampler.TickCounter; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.concurrent.TimeUnit; - -/** - * Calculates the servers TPS (ticks per second) rate. - * - *

The code use to calculate the TPS is the same as the code used by the Minecraft server itself. - * This means that this class will output values the same as the /tps command.

- * - *

We calculate our own values instead of pulling them from the server for two reasons. Firstly, - * it's easier - pulling from the server requires reflection code on each of the platforms, we'd - * rather avoid that. Secondly, it allows us to generate rolling averages over a shorter period of - * time.

- */ -public class TpsCalculator implements TickCounter.TickTask { - - private static final long SEC_IN_NANO = TimeUnit.SECONDS.toNanos(1); - private static final int TPS = 20; - private static final int SAMPLE_INTERVAL = 20; - private static final BigDecimal TPS_BASE = new BigDecimal(SEC_IN_NANO).multiply(new BigDecimal(SAMPLE_INTERVAL)); - - private final TpsRollingAverage avg5Sec = new TpsRollingAverage(5); - private final TpsRollingAverage avg10Sec = new TpsRollingAverage(10); - private final TpsRollingAverage avg1Min = new TpsRollingAverage(60); - private final TpsRollingAverage avg5Min = new TpsRollingAverage(60 * 5); - private final TpsRollingAverage avg15Min = new TpsRollingAverage(60 * 15); - - private final TpsRollingAverage[] averages = new TpsRollingAverage[]{ - this.avg5Sec, this.avg10Sec, this.avg1Min, this.avg5Min, this.avg15Min - }; - - private long last = 0; - - @Override - public void onTick(TickCounter counter) { - if (counter.getCurrentTick() % SAMPLE_INTERVAL != 0) { - return; - } - - long now = System.nanoTime(); - - if (this.last == 0) { - this.last = now; - return; - } - - long diff = now - this.last; - BigDecimal currentTps = TPS_BASE.divide(new BigDecimal(diff), 30, RoundingMode.HALF_UP); - BigDecimal total = currentTps.multiply(new BigDecimal(diff)); - - for (TpsRollingAverage rollingAverage : this.averages) { - rollingAverage.add(currentTps, diff, total); - } - - this.last = now; - } - - public double avg5Sec() { - return this.avg5Sec.getAverage(); - } - - public double avg10Sec() { - return this.avg10Sec.getAverage(); - } - - public double avg1Min() { - return this.avg1Min.getAverage(); - } - - public double avg5Min() { - return this.avg5Min.getAverage(); - } - - public double avg15Min() { - return this.avg15Min.getAverage(); - } - - - - /** - * Rolling average calculator taken. - * - *

This code is taken from PaperMC/Paper, licensed under MIT.

- * - * @author aikar (PaperMC) https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0021-Further-improve-server-tick-loop.patch - */ - public static final class TpsRollingAverage { - private final int size; - private long time; - private BigDecimal total; - private int index = 0; - private final BigDecimal[] samples; - private final long[] times; - - TpsRollingAverage(int size) { - this.size = size; - this.time = size * SEC_IN_NANO; - this.total = new BigDecimal(TPS).multiply(new BigDecimal(SEC_IN_NANO)).multiply(new BigDecimal(size)); - this.samples = new BigDecimal[size]; - this.times = new long[size]; - for (int i = 0; i < size; i++) { - this.samples[i] = new BigDecimal(TPS); - this.times[i] = SEC_IN_NANO; - } - } - - public void add(BigDecimal x, long t, BigDecimal total) { - this.time -= this.times[this.index]; - this.total = this.total.subtract(this.samples[this.index].multiply(new BigDecimal(this.times[this.index]))); - this.samples[this.index] = x; - this.times[this.index] = t; - this.time += t; - this.total = this.total.add(total); - if (++this.index == this.size) { - this.index = 0; - } - } - - public double getAverage() { - return this.total.divide(new BigDecimal(this.time), 30, RoundingMode.HALF_UP).doubleValue(); - } - } - -} diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/AbstractTickCounter.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/AbstractTickCounter.java deleted file mode 100644 index 4633024..0000000 --- a/spark-common/src/main/java/me/lucko/spark/common/sampler/AbstractTickCounter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package me.lucko.spark.common.sampler; - -import java.util.HashSet; -import java.util.Set; - -public abstract class AbstractTickCounter implements TickCounter { - - private final Set tasks = new HashSet<>(); - private int tick = 0; - - protected void onTick() { - for (TickTask r : this.tasks) { - r.onTick(this); - } - this.tick++; - } - - @Override - public int getCurrentTick() { - return this.tick; - } - - @Override - public void addTickTask(TickTask runnable) { - this.tasks.add(runnable); - } - - @Override - public void removeTickTask(TickTask runnable) { - this.tasks.remove(runnable); - } - -} diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java index 81a757a..56093c0 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java +++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java @@ -27,6 +27,7 @@ import me.lucko.spark.common.sampler.aggregator.DataAggregator; import me.lucko.spark.common.sampler.aggregator.SimpleDataAggregator; import me.lucko.spark.common.sampler.aggregator.TickedDataAggregator; import me.lucko.spark.common.sampler.node.ThreadNode; +import me.lucko.spark.common.sampler.tick.TickHook; import me.lucko.spark.proto.SparkProtos.SamplerData; import me.lucko.spark.proto.SparkProtos.SamplerMetadata; @@ -86,9 +87,9 @@ public class Sampler implements Runnable { this.endTime = endTime; } - public Sampler(int interval, ThreadDumper threadDumper, ThreadGrouper threadGrouper, long endTime, boolean includeLineNumbers, boolean ignoreSleeping, TickCounter tickCounter, int tickLengthThreshold) { + public Sampler(int interval, ThreadDumper threadDumper, ThreadGrouper threadGrouper, long endTime, boolean includeLineNumbers, boolean ignoreSleeping, TickHook tickHook, int tickLengthThreshold) { this.threadDumper = threadDumper; - this.dataAggregator = new TickedDataAggregator(this.workerPool, threadGrouper, interval, includeLineNumbers, ignoreSleeping, tickCounter, tickLengthThreshold); + this.dataAggregator = new TickedDataAggregator(this.workerPool, threadGrouper, interval, includeLineNumbers, ignoreSleeping, tickHook, tickLengthThreshold); this.interval = interval; this.endTime = endTime; } diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java index 4808214..ae7fb89 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java +++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java @@ -20,6 +20,8 @@ package me.lucko.spark.common.sampler; +import me.lucko.spark.common.sampler.tick.TickHook; + import java.util.concurrent.TimeUnit; /** @@ -35,7 +37,7 @@ public class SamplerBuilder { private ThreadGrouper threadGrouper = ThreadGrouper.BY_NAME; private int ticksOver = -1; - private TickCounter tickCounter = null; + private TickHook tickHook = null; public SamplerBuilder() { } @@ -63,9 +65,9 @@ public class SamplerBuilder { return this; } - public SamplerBuilder ticksOver(int ticksOver, TickCounter tickCounter) { + public SamplerBuilder ticksOver(int ticksOver, TickHook tickHook) { this.ticksOver = ticksOver; - this.tickCounter = tickCounter; + this.tickHook = tickHook; return this; } @@ -83,10 +85,10 @@ public class SamplerBuilder { Sampler sampler; int intervalMicros = (int) (this.samplingInterval * 1000d); - if (this.ticksOver == -1 || this.tickCounter == null) { + if (this.ticksOver == -1 || this.tickHook == null) { sampler = new Sampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.includeLineNumbers, this.ignoreSleeping); } else { - sampler = new Sampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.includeLineNumbers, this.ignoreSleeping, this.tickCounter, this.ticksOver); + sampler = new Sampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.includeLineNumbers, this.ignoreSleeping, this.tickHook, this.ticksOver); } sampler.start(); diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/TickCounter.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/TickCounter.java deleted file mode 100644 index aa839ba..0000000 --- a/spark-common/src/main/java/me/lucko/spark/common/sampler/TickCounter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of spark. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package me.lucko.spark.common.sampler; - -/** - * A hook with the game's "tick loop". - */ -public interface TickCounter extends AutoCloseable { - - /** - * Starts the counter - */ - void start(); - - /** - * Stops the counter - */ - @Override - void close(); - - /** - * Gets the current tick number - * - * @return the current tick - */ - int getCurrentTick(); - - /** - * Adds a task to be called each time the tick increments - * - * @param runnable the task - */ - void addTickTask(TickTask runnable); - - /** - * Removes a tick task - * - * @param runnable the task - */ - void removeTickTask(TickTask runnable); - - interface TickTask { - void onTick(TickCounter counter); - } - -} diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/TickedDataAggregator.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/TickedDataAggregator.java