aboutsummaryrefslogtreecommitdiff
path: root/spark-common/src/main/java/me/lucko/spark
diff options
context:
space:
mode:
authorLuck <git@lucko.me>2020-02-04 00:49:40 +0000
committerLuck <git@lucko.me>2020-02-04 00:49:40 +0000
commite02d52ce8d45550a4d77f11971e31cf0732e5f0c (patch)
treecfcfb2850ff6b279276e43233e5e1acf82993a98 /spark-common/src/main/java/me/lucko/spark
parentd15a12788ddc8aba09f49003fcef55b927850de3 (diff)
downloadspark-e02d52ce8d45550a4d77f11971e31cf0732e5f0c.tar.gz
spark-e02d52ce8d45550a4d77f11971e31cf0732e5f0c.tar.bz2
spark-e02d52ce8d45550a4d77f11971e31cf0732e5f0c.zip
Monitor average tick durations & report them in /spark tps
Diffstat (limited to 'spark-common/src/main/java/me/lucko/spark')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java44
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java20
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java81
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java10
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java22
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java16
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java (renamed from spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java)92
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java5
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java12
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/TickedDataAggregator.java10
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickHook.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/AbstractTickCounter.java)12
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java45
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickHook.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/TickCounter.java)22
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java57
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java42
15 files changed, 369 insertions, 121 deletions
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<CommandModule> commandModules;
private final List<Command> 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.
*
* <p>Returns {@code null} if the platform does not have "ticks"</p>
*
- * @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.
+ *
+ * <p>Returns {@code null} if the platform does not have "ticks"</p>
+ *
+ * @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<Component> 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/TpsCalculator.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java
index 6b4f2d1..c91ceae 100644
--- 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/TickStatistics.java
@@ -20,7 +20,9 @@
package me.lucko.spark.common.monitor.tick;
-import me.lucko.spark.common.sampler.TickCounter;
+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;
@@ -37,28 +39,35 @@ import java.util.concurrent.TimeUnit;
* rather avoid that. Secondly, it allows us to generate rolling averages over a shorter period of
* time.</p>
*/
-public class TpsCalculator implements TickCounter.TickTask {
+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 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 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(TickCounter counter) {
- if (counter.getCurrentTick() % SAMPLE_INTERVAL != 0) {
+ public void onTick(TickHook hook) {
+ if (hook.getCurrentTick() % TPS_SAMPLE_INTERVAL != 0) {
return;
}
@@ -73,33 +82,62 @@ public class TpsCalculator implements TickCounter.TickTask {
BigDecimal currentTps = TPS_BASE.divide(new BigDecimal(diff), 30, RoundingMode.HALF_UP);
BigDecimal total = currentTps.multiply(new BigDecimal(diff));
- for (TpsRollingAverage rollingAverage : this.averages) {
+ for (TpsRollingAverage rollingAverage : this.tpsAverages) {
rollingAverage.add(currentTps, diff, total);
}
this.last = now;
}
- public double avg5Sec() {
- return this.avg5Sec.getAverage();
+ @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 avg10Sec() {
- return this.avg10Sec.getAverage();
+ public double tps10Sec() {
+ return this.tps10Sec.getAverage();
}
- public double avg1Min() {
- return this.avg1Min.getAverage();
+ public double tps1Min() {
+ return this.tps1Min.getAverage();
}
- public double avg5Min() {
- return this.avg5Min.getAverage();
+ public double tps5Min() {
+ return this.tps5Min.getAverage();
}
- public double avg15Min() {
- return this.avg15Min.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;
+ }
/**
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/aggregator/TickedDataAggregator.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/TickedDataAggregator.java
index a4dfdb8..d8fda73 100644
--- 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
@@ -21,8 +21,8 @@
package me.lucko.spark.common.sampler.aggregator;
import me.lucko.spark.common.sampler.ThreadGrouper;
-import me.lucko.spark.common.sampler.TickCounter;
import me.lucko.spark.common.sampler.node.ThreadNode;
+import me.lucko.spark.common.sampler.tick.TickHook;
import me.lucko.spark.proto.SparkProtos.SamplerMetadata;
import java.lang.management.ThreadInfo;
@@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit;
public class TickedDataAggregator extends AbstractDataAggregator {
/** Used to monitor the current "tick" of the server */
- private final TickCounter tickCounter;
+ private final TickHook tickHook;
/** Tick durations under this threshold will not be inserted, measured in microseconds */
private final long tickLengthThreshold;
@@ -53,9 +53,9 @@ public class TickedDataAggregator extends AbstractDataAggregator {
private int currentTick = -1;
private TickList currentData = new TickList(0);
- public TickedDataAggregator(ExecutorService workerPool, ThreadGrouper threadGrouper, int interval, boolean includeLineNumbers, boolean ignoreSleeping, TickCounter tickCounter, int tickLengthThreshold) {
+ public TickedDataAggregator(ExecutorService workerPool, ThreadGrouper threadGrouper, int interval, boolean includeLineNumbers, boolean ignoreSleeping, TickHook tickHook, int tickLengthThreshold) {
super(workerPool, threadGrouper, interval, includeLineNumbers, ignoreSleeping);
- this.tickCounter = tickCounter;
+ this.tickHook = tickHook;
this.tickLengthThreshold = TimeUnit.MILLISECONDS.toMicros(tickLengthThreshold);
// 50 millis in a tick, plus 10 so we have a bit of room to go over
double intervalMilliseconds = interval / 1000d;
@@ -74,7 +74,7 @@ public class TickedDataAggregator extends AbstractDataAggregator {
@Override
public void insertData(ThreadInfo threadInfo) {
synchronized (this.mutex) {
- int tick = this.tickCounter.getCurrentTick();
+ int tick = this.tickHook.getCurrentTick();
if (this.currentTick != tick) {
pushCurrentTick();
this.currentTick = tick;
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/tick/AbstractTickHook.java
index 4633024..b4e5054 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/AbstractTickCounter.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickHook.java
@@ -18,18 +18,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package me.lucko.spark.common.sampler;
+package me.lucko.spark.common.sampler.tick;
import java.util.HashSet;
import java.util.Set;
-public abstract class AbstractTickCounter implements TickCounter {
+public abstract class AbstractTickHook implements TickHook {
- private final Set<TickTask> tasks = new HashSet<>();
+ private final Set<Callback> tasks = new HashSet<>();
private int tick = 0;
protected void onTick() {
- for (TickTask r : this.tasks) {
+ for (Callback r : this.tasks) {
r.onTick(this);
}
this.tick++;
@@ -41,12 +41,12 @@ public abstract class AbstractTickCounter implements TickCounter {
}
@Override
- public void addTickTask(TickTask runnable) {
+ public void addCallback(Callback runnable) {
this.tasks.add(runnable);
}
@Override
- public void removeTickTask(TickTask runnable) {
+ public void removeCallback(Callback runnable) {
this.tasks.remove(runnable);
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java
new file mode 100644
index 0000000..4005f87
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java
@@ -0,0 +1,45 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.common.sampler.tick;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class AbstractTickReporter implements TickReporter {
+ private final Set<Callback> tasks = new HashSet<>();
+
+ protected void onTick(double duration) {
+ for (Callback r : this.tasks) {
+ r.onTick(duration);
+ }
+ }
+
+ @Override
+ public void addCallback(Callback runnable) {
+ this.tasks.add(runnable);
+ }
+
+ @Override
+ public void removeCallback(Callback runnable) {
+ this.tasks.remove(runnable);
+ }
+
+}
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/tick/TickHook.java
index aa839ba..a0dd425 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/TickCounter.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickHook.java
@@ -18,20 +18,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package me.lucko.spark.common.sampler;
+package me.lucko.spark.common.sampler.tick;
/**
* A hook with the game's "tick loop".
*/
-public interface TickCounter extends AutoCloseable {
+public interface TickHook extends AutoCloseable {
/**
- * Starts the counter
+ * Starts the hook
*/
void start();
/**
- * Stops the counter
+ * Stops the hook
*/
@Override
void close();
@@ -44,21 +44,21 @@ public interface TickCounter extends AutoCloseable {
int getCurrentTick();
/**
- * Adds a task to be called each time the tick increments
+ * Adds a callback to be called each time the tick increments
*
* @param runnable the task
*/
- void addTickTask(TickTask runnable);
+ void addCallback(Callback runnable);
/**
- * Removes a tick task
+ * Removes a callback
*
- * @param runnable the task
+ * @param runnable the callback
*/
- void removeTickTask(TickTask runnable);
+ void removeCallback(Callback runnable);
- interface TickTask {
- void onTick(TickCounter counter);
+ interface Callback {
+ void onTick(TickHook hook);
}
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java
new file mode 100644
index 0000000..e922e72
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java
@@ -0,0 +1,57 @@
+/*
+ * This file is part of spark.
+ *
+ * Copyright (c) lucko (Luck) <luck@lucko.me>
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package me.lucko.spark.common.sampler.tick;
+
+/**
+ * A reporting callback for the game's "tick loop".
+ */
+public interface TickReporter extends AutoCloseable {
+
+ /**
+ * Starts the reporter
+ */
+ void start();
+
+ /**
+ * Stops the reporter
+ */
+ @Override
+ void close();
+
+ /**
+ * Adds a callback to be called each time the tick increments
+ *
+ * @param runnable the callback
+ */
+ void addCallback(Callback runnable);
+
+ /**
+ * Removes a callback
+ *
+ * @param runnable callback
+ */
+ void removeCallback(Callback runnable);
+
+ interface Callback {
+ void onTick(double duration);
+ }
+
+}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java b/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java
index 514876d..5cf5bb5 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java
@@ -37,18 +37,46 @@ public class RollingAverage {
}
public void add(BigDecimal num) {
- this.total = this.total.add(num);
- this.samples.add(num);
- if (this.samples.size() > this.size) {
- this.total = this.total.subtract(this.samples.remove());
+ synchronized (this) {
+ this.total = this.total.add(num);
+ this.samples.add(num);
+ if (this.samples.size() > this.size) {
+ this.total = this.total.subtract(this.samples.remove());
+ }
}
}
public double getAverage() {
- if (this.samples.isEmpty()) {
- return 0;
+ synchronized (this) {
+ if (this.samples.isEmpty()) {
+ return 0;
+ }
+ return this.total.divide(BigDecimal.valueOf(this.samples.size()), 30, RoundingMode.HALF_UP).doubleValue();
+ }
+ }
+
+ public double getMax() {
+ synchronized (this) {
+ BigDecimal max = BigDecimal.ZERO;
+ for (BigDecimal sample : this.samples) {
+ if (sample.compareTo(max) > 0) {
+ max = sample;
+ }
+ }
+ return max.doubleValue();
+ }
+ }
+
+ public double getMin() {
+ synchronized (this) {
+ BigDecimal min = BigDecimal.ZERO;
+ for (BigDecimal sample : this.samples) {
+ if (min == BigDecimal.ZERO || sample.compareTo(min) < 0) {
+ min = sample;
+ }
+ }
+ return min.doubleValue();
}
- return this.total.divide(BigDecimal.valueOf(this.samples.size()), 30, RoundingMode.HALF_UP).doubleValue();
}
}