From 6f2153e33d232d5b52418531abc0007dcdc466e9 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 5 Apr 2020 15:46:51 +0100 Subject: Allow exact tick duration to be used as threshold in tickmonitoring command (#43) --- .../command/modules/TickMonitoringModule.java | 22 +++-- .../spark/common/monitor/tick/TickMonitor.java | 95 ++++++++++++++++++---- 2 files changed, 93 insertions(+), 24 deletions(-) (limited to 'spark-common') 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 245a55b..7014770 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,6 +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.monitor.tick.TickMonitor.ReportPredicate; import me.lucko.spark.common.sampler.tick.TickHook; import net.kyori.text.Component; import net.kyori.text.TextComponent; @@ -53,6 +54,7 @@ public class TickMonitoringModule implements CommandModule { consumer.accept(Command.builder() .aliases("tickmonitoring") .argumentUsage("threshold", "percentage increase") + .argumentUsage("threshold-tick", "tick duration") .argumentUsage("without-gc", null) .executor((platform, sender, resp, arguments) -> { if (this.tickHook == null) { @@ -64,19 +66,25 @@ public class TickMonitoringModule implements CommandModule { } if (this.activeTickMonitor == null) { - int threshold = arguments.intFlag("threshold"); - if (threshold == -1) { - threshold = 100; + ReportPredicate reportPredicate; + + int threshold; + if ((threshold = arguments.intFlag("threshold")) != -1) { + reportPredicate = new ReportPredicate.PercentageChangeGt(threshold); + } else if ((threshold = arguments.intFlag("threshold-tick")) != -1) { + reportPredicate = new ReportPredicate.DurationGt(threshold); + } else { + reportPredicate = new ReportPredicate.PercentageChangeGt(100); } - this.activeTickMonitor = new ReportingTickMonitor(platform, resp, this.tickHook, threshold, !arguments.boolFlag("without-gc")); + this.activeTickMonitor = new ReportingTickMonitor(platform, resp, this.tickHook, reportPredicate, !arguments.boolFlag("without-gc")); this.tickHook.addCallback(this.activeTickMonitor); } else { close(); resp.broadcastPrefixed(TextComponent.of("Tick monitor disabled.")); } }) - .tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--threshold", "--without-gc")) + .tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--threshold", "--threshold-tick", "--without-gc")) .build() ); } @@ -84,8 +92,8 @@ public class TickMonitoringModule implements CommandModule { private static class ReportingTickMonitor extends TickMonitor { private final CommandResponseHandler resp; - ReportingTickMonitor(SparkPlatform platform, CommandResponseHandler resp, TickHook tickHook, int percentageChangeThreshold, boolean monitorGc) { - super(platform, tickHook, percentageChangeThreshold, monitorGc); + ReportingTickMonitor(SparkPlatform platform, CommandResponseHandler resp, TickHook tickHook, ReportPredicate reportPredicate, boolean monitorGc) { + super(platform, tickHook, reportPredicate, 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 8b0befe..4a755d1 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 @@ -38,7 +38,7 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio private final TickHook tickHook; private final int zeroTick; private final GarbageCollectionMonitor garbageCollectionMonitor; - private final int percentageChangeThreshold; + private final ReportPredicate reportPredicate; // data private volatile double lastTickTime = 0; @@ -46,11 +46,11 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio private final DoubleSummaryStatistics averageTickTime = new DoubleSummaryStatistics(); private double avg; - public TickMonitor(SparkPlatform platform, TickHook tickHook, int percentageChangeThreshold, boolean monitorGc) { + public TickMonitor(SparkPlatform platform, TickHook tickHook, ReportPredicate reportPredicate, boolean monitorGc) { this.platform = platform; this.tickHook = tickHook; this.zeroTick = tickHook.getCurrentTick(); - this.percentageChangeThreshold = percentageChangeThreshold; + this.reportPredicate = reportPredicate; if (monitorGc) { this.garbageCollectionMonitor = new GarbageCollectionMonitor(); @@ -88,17 +88,16 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio // find the diff double last = this.lastTickTime; - double diff = now - last; - boolean ignore = last == 0; + double tickDuration = now - last; this.lastTickTime = now; - if (ignore) { + if (last == 0) { return; } // form averages if (this.state == State.SETUP) { - this.averageTickTime.accept(diff); + this.averageTickTime.accept(tickDuration); // move onto the next state if (this.averageTickTime.getCount() >= 120) { @@ -123,13 +122,12 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio sendMessage(TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of(">", TextColor.WHITE)) .append(TextComponent.space()) - .append(TextComponent.of("Avg: ")) + .append(TextComponent.of("Average: ")) .append(TextComponent.of(df.format(this.averageTickTime.getAverage()))) .append(TextComponent.of("ms")) .build() ); - sendMessage(TextComponent.of("Starting now, any ticks with >" + this.percentageChangeThreshold + "% increase in " + - "duration compared to the average will be reported.")); + sendMessage(this.reportPredicate.monitoringStartMessage()); }); this.avg = this.averageTickTime.getAverage(); @@ -138,19 +136,15 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio } if (this.state == State.MONITORING) { - double increase = diff - this.avg; - if (increase <= 0) { - return; - } - + double increase = tickDuration - this.avg; double percentageChange = (increase * 100d) / this.avg; - if (percentageChange > this.percentageChangeThreshold) { + if (this.reportPredicate.shouldReport(tickDuration, increase, percentageChange)) { this.platform.getPlugin().executeAsync(() -> { sendMessage(TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of("Tick ")) .append(TextComponent.of("#" + getCurrentTick(), TextColor.DARK_GRAY)) .append(TextComponent.of(" lasted ")) - .append(TextComponent.of(df.format(diff), TextColor.GOLD)) + .append(TextComponent.of(df.format(tickDuration), TextColor.GOLD)) .append(TextComponent.of(" ms. ")) .append(TextComponent.of("(")) .append(TextComponent.of(df.format(percentageChange) + "%", TextColor.GOLD)) @@ -193,6 +187,73 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio }); } + /** + * A predicate to test whether a tick should be reported. + */ + public interface ReportPredicate { + + /** + * Tests whether a tick should be reported. + * + * @param duration the tick duration + * @param increaseFromAvg the difference between the ticks duration and the average + * @param percentageChange the percentage change between the ticks duration and the average + * @return true if the tick should be reported, false otherwise + */ + boolean shouldReport(double duration, double increaseFromAvg, double percentageChange); + + /** + * Gets a component to describe how the predicate will select ticks to report. + * + * @return the component + */ + Component monitoringStartMessage(); + + final class PercentageChangeGt implements ReportPredicate { + private final double threshold; + + public PercentageChangeGt(double threshold) { + this.threshold = threshold; + } + + @Override + public boolean shouldReport(double duration, double increaseFromAvg, double percentageChange) { + if (increaseFromAvg <= 0) { + return false; + } + return percentageChange > this.threshold; + } + + @Override + public Component monitoringStartMessage() { + return TextComponent.of("Starting now, any ticks with >" + this.threshold + "% increase in " + + "duration compared to the average will be reported."); + } + } + + final class DurationGt implements ReportPredicate { + private final double threshold; + + public DurationGt(double threshold) { + this.threshold = threshold; + } + + @Override + public boolean shouldReport(double duration, double increaseFromAvg, double percentageChange) { + if (increaseFromAvg <= 0) { + return false; + } + return duration > this.threshold; + } + + @Override + public Component monitoringStartMessage() { + return TextComponent.of("Starting now, any ticks with duration >" + this.threshold + " will be reported."); + } + } + + } + private enum State { SETUP, MONITORING -- cgit