aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuck <git@lucko.me>2021-04-07 11:51:03 +0100
committerLuck <git@lucko.me>2021-04-07 11:51:03 +0100
commit9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5 (patch)
treea2e153c4d723216f95c49153d5afef524dea9531
parentbb3046600f1d1a98a69f918dd59dff64e051480b (diff)
downloadspark-9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5.tar.gz
spark-9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5.tar.bz2
spark-9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5.zip
Add a basic API for retrieving spark statistics
-rw-r--r--settings.gradle1
-rw-r--r--spark-api/build.gradle32
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/Spark.java79
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/SparkProvider.java53
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/gc/GarbageCollector.java73
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/statistic/Statistic.java46
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java104
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/statistic/misc/DoubleAverageInfo.java75
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/statistic/types/DoubleStatistic.java50
-rw-r--r--spark-api/src/main/java/me/lucko/spark/api/statistic/types/GenericStatistic.java51
-rw-r--r--spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitSparkPlugin.java7
-rw-r--r--spark-common/build.gradle2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java9
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java5
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/api/AbstractStatistic.java85
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/api/GarbageCollectorInfo.java69
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java189
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java8
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/cpu/CpuMonitor.java12
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java20
-rw-r--r--spark-nukkit/src/main/java/me/lucko/spark/nukkit/NukkitSparkPlugin.java7
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java6
22 files changed, 964 insertions, 19 deletions
diff --git a/settings.gradle b/settings.gradle
index 00a2eed..239b70a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -11,6 +11,7 @@ pluginManagement {
rootProject.name = 'spark'
include (
+ 'spark-api',
'spark-common',
'spark-bukkit',
'spark-bungeecord',
diff --git a/spark-api/build.gradle b/spark-api/build.gradle
new file mode 100644
index 0000000..31458af
--- /dev/null
+++ b/spark-api/build.gradle
@@ -0,0 +1,32 @@
+plugins {
+ id 'maven-publish'
+}
+
+version = '0.1-SNAPSHOT'
+
+dependencies {
+ compileOnly 'org.checkerframework:checker-qual:3.8.0'
+ compileOnly 'org.jetbrains:annotations:20.1.0'
+}
+
+publishing {
+ //repositories {
+ // maven {
+ // url = 'https://oss.sonatype.org/content/repositories/snapshots'
+ // credentials {
+ // username = sonatypeUsername
+ // password = sonatypePassword
+ // }
+ // }
+ //}
+ publications {
+ mavenJava(MavenPublication) {
+ from components.java
+ pom {
+ name = 'spark'
+ description = 'spark is a performance profiling plugin/mod for Minecraft clients, servers and proxies.'
+ url = 'https://spark.lucko.me/'
+ }
+ }
+ }
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/Spark.java b/spark-api/src/main/java/me/lucko/spark/api/Spark.java
new file mode 100644
index 0000000..1f4ee64
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/Spark.java
@@ -0,0 +1,79 @@
+/*
+ * 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.api;
+
+import me.lucko.spark.api.gc.GarbageCollector;
+import me.lucko.spark.api.statistic.misc.DoubleAverageInfo;
+import me.lucko.spark.api.statistic.types.DoubleStatistic;
+import me.lucko.spark.api.statistic.types.GenericStatistic;
+import me.lucko.spark.api.statistic.StatisticWindow;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.Unmodifiable;
+
+import java.util.Map;
+
+/**
+ * The spark API.
+ */
+public interface Spark {
+
+ /**
+ * Gets the CPU usage statistic for the current process.
+ *
+ * @return the CPU process statistic
+ */
+ @NonNull DoubleStatistic<StatisticWindow.CpuUsage> cpuProcess();
+
+ /**
+ * Gets the CPU usage statistic for the overall system.
+ *
+ * @return the CPU system statistic
+ */
+ @NonNull DoubleStatistic<StatisticWindow.CpuUsage> cpuSystem();
+
+ /**
+ * Gets the ticks per second statistic.
+ *
+ * <p>Returns {@code null} if the statistic is not supported.</p>
+ *
+ * @return the ticks per second statistic
+ */
+ @Nullable DoubleStatistic<StatisticWindow.TicksPerSecond> tps();
+
+ /**
+ * Gets the milliseconds per tick statistic.
+ *
+ * <p>Returns {@code null} if the statistic is not supported.</p>
+ *
+ * @return the milliseconds per tick statistic
+ */
+ @Nullable GenericStatistic<DoubleAverageInfo, StatisticWindow.MillisPerTick> mspt();
+
+ /**
+ * Gets the garbage collector statistics.
+ *
+ * @return the garbage collector statistics
+ */
+ @NonNull @Unmodifiable Map<String, GarbageCollector> gc();
+
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/SparkProvider.java b/spark-api/src/main/java/me/lucko/spark/api/SparkProvider.java
new file mode 100644
index 0000000..a93c27d
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/SparkProvider.java
@@ -0,0 +1,53 @@
+/*
+ * 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.api;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * Singleton provider for {@link Spark}.
+ */
+public final class SparkProvider {
+
+ private static Spark instance;
+
+ /**
+ * Gets the singleton spark API instance.
+ *
+ * @return the spark API instance
+ */
+ public static @NonNull Spark get() {
+ Spark instance = SparkProvider.instance;
+ if (instance == null) {
+ throw new IllegalStateException("spark has not loaded yet!");
+ }
+ return instance;
+ }
+
+ static void set(Spark impl) {
+ SparkProvider.instance = impl;
+ }
+
+ private SparkProvider() {
+ throw new AssertionError();
+ }
+
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/gc/GarbageCollector.java b/spark-api/src/main/java/me/lucko/spark/api/gc/GarbageCollector.java
new file mode 100644
index 0000000..b2c8801
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/gc/GarbageCollector.java
@@ -0,0 +1,73 @@
+/*
+ * 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.api.gc;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * Statistics about a garbage collector.
+ *
+ * <p>All time durations are measured in milliseconds.</p>
+ */
+public interface GarbageCollector {
+
+ /**
+ * Gets the name of the garbage collector.
+ *
+ * @return the name
+ */
+ @NonNull String name();
+
+ /**
+ * Gets the total number of collections performed.
+ *
+ * @return the total number of collections
+ */
+ long totalCollections();
+
+ /**
+ * Gets the total amount of time spent performing collections.
+ *
+ * <p>Measured in milliseconds.</p>
+ *
+ * @return the total time spent collecting
+ */
+ long totalTime();
+
+ /**
+ * Gets the average amount of time spent performing each collection.
+ *
+ * <p>Measured in milliseconds.</p>
+ *
+ * @return the average collection time
+ */
+ double avgTime();
+
+ /**
+ * Gets the average frequency at which collections are performed.
+ *
+ * <p>Measured in milliseconds.</p>
+ *
+ * @return the average frequency of collections
+ */
+ long avgFrequency();
+
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/statistic/Statistic.java b/spark-api/src/main/java/me/lucko/spark/api/statistic/Statistic.java
new file mode 100644
index 0000000..26af8e1
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/statistic/Statistic.java
@@ -0,0 +1,46 @@
+/*
+ * 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.api.statistic;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * A statistic for which a rolling average in various windows is recorded.
+ *
+ * @param <W> the window type
+ */
+public interface Statistic<W extends Enum<W> & StatisticWindow> {
+
+ /**
+ * Gets the statistic name.
+ *
+ * @return the statistic name
+ */
+ @NonNull String name();
+
+ /**
+ * Gets the windows used for rolling averages.
+ *
+ * @return the windows
+ */
+ @NonNull W[] getWindows();
+
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java b/spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java
new file mode 100644
index 0000000..cfae9dd
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/statistic/StatisticWindow.java
@@ -0,0 +1,104 @@
+/*
+ * 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.api.statistic;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.NotNull;
+
+import java.time.Duration;
+
+/**
+ * A window in which a statistic is recorded as a rolling average.
+ */
+public interface StatisticWindow {
+
+ /**
+ * Gets the length of the window as a {@link Duration}.
+ *
+ * @return the length of the window
+ */
+ @NonNull Duration length();
+
+ /**
+ * The {@link StatisticWindow} used for CPU usage.
+ */
+ enum CpuUsage implements StatisticWindow {
+
+ SECONDS_10(Duration.ofSeconds(10)),
+ MINUTES_1(Duration.ofMinutes(1)),
+ MINUTES_15(Duration.ofMinutes(15));
+
+ private final Duration value;
+
+ CpuUsage(Duration value) {
+ this.value = value;
+ }
+
+ @Override
+ public @NotNull Duration length() {
+ return this.value;
+ }
+ }
+
+ /**
+ * The {@link StatisticWindow} used for TPS.
+ */
+ enum TicksPerSecond implements StatisticWindow {
+
+ SECONDS_5(Duration.ofSeconds(5)),
+ SECONDS_10(Duration.ofSeconds(10)),
+ MINUTES_1(Duration.ofMinutes(1)),
+ MINUTES_5(Duration.ofMinutes(5)),
+ MINUTES_15(Duration.ofMinutes(15));
+
+ private final Duration value;
+
+ TicksPerSecond(Duration value) {
+ this.value = value;
+ }
+
+ @Override
+ public @NotNull Duration length() {
+ return this.value;
+ }
+ }
+
+ /**
+ * The {@link StatisticWindow} used for MSPT.
+ */
+ enum MillisPerTick implements StatisticWindow {
+
+ SECONDS_10(Duration.ofSeconds(10)),
+ MINUTES_1(Duration.ofMinutes(1));
+
+ private final Duration value;
+
+ MillisPerTick(Duration value) {
+ this.value = value;
+ }
+
+ @Override
+ public @NotNull Duration length() {
+ return this.value;
+ }
+ }
+
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/statistic/misc/DoubleAverageInfo.java b/spark-api/src/main/java/me/lucko/spark/api/statistic/misc/DoubleAverageInfo.java
new file mode 100644
index 0000000..0f882de
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/statistic/misc/DoubleAverageInfo.java
@@ -0,0 +1,75 @@
+/*
+ * 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.api.statistic.misc;
+
+/**
+ * Statistics for a recorded double value.
+ */
+public interface DoubleAverageInfo {
+
+ /**
+ * Gets the mean value.
+ *
+ * @return the mean
+ */
+ double mean();
+
+ /**
+ * Gets the max value.
+ *
+ * @return the max
+ */
+ double max();
+
+ /**
+ * Gets the min value.
+ *
+ * @return the min
+ */
+ double min();
+
+ /**
+ * Gets the median value.
+ *
+ * @return the median
+ */
+ default double median() {
+ return percentile(0.50d);
+ }
+
+ /**
+ * Gets the 95th percentile value.
+ *
+ * @return the 95th percentile
+ */
+ default double percentile95th() {
+ return percentile(0.95d);
+ }
+
+ /**
+ * Gets the average value at a given percentile.
+ *
+ * @param percentile the percentile, as a double between 0 and 1.
+ * @return the average value at the given percentile
+ */
+ double percentile(double percentile);
+
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/statistic/types/DoubleStatistic.java b/spark-api/src/main/java/me/lucko/spark/api/statistic/types/DoubleStatistic.java
new file mode 100644
index 0000000..ab26b7b
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/statistic/types/DoubleStatistic.java
@@ -0,0 +1,50 @@
+/*
+ * 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.api.statistic.types;
+
+import me.lucko.spark.api.statistic.Statistic;
+import me.lucko.spark.api.statistic.StatisticWindow;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * A {@link Statistic} with a {@code double} value.
+ *
+ * @param <W> the window type
+ */
+public interface DoubleStatistic<W extends Enum<W> & StatisticWindow> extends Statistic<W> {
+
+ /**
+ * Polls the current value of the statistic in the given window.
+ *
+ * @param window the window
+ * @return the value
+ */
+ double poll(@NonNull W window);
+
+ /**
+ * Polls the current values of the statistic in all windows.
+ *
+ * @return the values
+ */
+ double[] poll();
+
+}
diff --git a/spark-api/src/main/java/me/lucko/spark/api/statistic/types/GenericStatistic.java b/spark-api/src/main/java/me/lucko/spark/api/statistic/types/GenericStatistic.java
new file mode 100644
index 0000000..2c27c05
--- /dev/null
+++ b/spark-api/src/main/java/me/lucko/spark/api/statistic/types/GenericStatistic.java
@@ -0,0 +1,51 @@
+/*
+ * 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.api.statistic.types;
+
+import me.lucko.spark.api.statistic.Statistic;
+import me.lucko.spark.api.statistic.StatisticWindow;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * A {@link Statistic} with a generic (non-primitive) type.
+ *
+ * @param <T> the generic value type
+ * @param <W> the window type
+ */
+public interface GenericStatistic<T, W extends Enum<W> & StatisticWindow> extends Statistic<W> {
+
+ /**
+ * Polls the current value of the statistic in the given window.
+ *
+ * @param window the window
+ * @return the value
+ */
+ T poll(@NonNull W window);
+
+ /**
+ * Polls the current values of the statistic in all windows.
+ *
+ * @return the values
+ */
+ T[] poll();
+
+}
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 77fa64c..970c4b4 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
@@ -20,6 +20,7 @@
package me.lucko.spark.bukkit;
+import me.lucko.spark.api.Spark;
import me.lucko.spark.bukkit.placeholder.SparkMVdWPlaceholders;
import me.lucko.spark.bukkit.placeholder.SparkPlaceholderApi;
import me.lucko.spark.common.SparkPlatform;
@@ -35,6 +36,7 @@ import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
+import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import java.nio.file.Path;
@@ -162,6 +164,11 @@ public class BukkitSparkPlugin extends JavaPlugin implements SparkPlugin {
return new BukkitPlatformInfo(getServer());
}
+ @Override
+ public void registerApi(Spark api) {
+ getServer().getServicesManager().register(Spark.class, api, this, ServicePriority.Normal);
+ }
+
private static boolean classExists(String className) {
try {
Class.forName(className);
diff --git a/spark-common/build.gradle b/spark-common/build.gradle
index ea4019d..8530a0e 100644
--- a/spark-common/build.gradle
+++ b/spark-common/build.gradle
@@ -3,6 +3,7 @@ plugins {
}
dependencies {
+ compile project(':spark-api')
compile 'com.github.jvm-profiling-tools:async-profiler:v2.0'
compile 'org.ow2.asm:asm:7.1'
compile 'com.google.protobuf:protobuf-javalite:3.15.6'
@@ -25,6 +26,7 @@ dependencies {
}
compileOnly 'com.google.code.gson:gson:2.7'
compileOnly 'com.google.guava:guava:19.0'
+ compileOnly 'org.checkerframework:checker-qual:3.8.0'
}
processResources {
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 c9aa030..099ad2d 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
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import me.lucko.spark.common.activitylog.ActivityLog;
+import me.lucko.spark.common.api.SparkApi;
import me.lucko.spark.common.command.Arguments;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
@@ -89,6 +90,8 @@ public class SparkPlatform {
private Map<String, GarbageCollectorStatistics> startupGcStatistics = ImmutableMap.of();
private long serverNormalOperationStartTime;
+ private SparkApi api;
+
public SparkPlatform(SparkPlugin plugin) {
this.plugin = plugin;
@@ -131,6 +134,10 @@ public class SparkPlatform {
this.startupGcStatistics = GarbageCollectorStatistics.pollStats();
this.serverNormalOperationStartTime = System.currentTimeMillis();
});
+
+ this.api = new SparkApi(this);
+ this.plugin.registerApi(this.api);
+ SparkApi.register(this.api);
}
public void disable() {
@@ -144,6 +151,8 @@ public class SparkPlatform {
for (CommandModule module : this.commandModules) {
module.close();
}
+
+ SparkApi.unregister();
}
public SparkPlugin getPlugin() {
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 171367e..216f23f 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
@@ -20,6 +20,7 @@
package me.lucko.spark.common;
+import me.lucko.spark.api.Spark;
import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.platform.PlatformInfo;
import me.lucko.spark.common.sampler.ThreadDumper;
@@ -107,4 +108,8 @@ public interface SparkPlugin {
*/
PlatformInfo getPlatformInfo();
+ default void registerApi(Spark api) {
+
+ }
+
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/api/AbstractStatistic.java b/spark-common/src/main/java/me/lucko/spark/common/api/AbstractStatistic.java
new file mode 100644
index 0000000..49a6ccb
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/api/AbstractStatistic.java
@@ -0,0 +1,85 @@
+/*
+ * 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.api;
+
+import me.lucko.spark.api.statistic.Statistic;
+import me.lucko.spark.api.statistic.StatisticWindow;
+import me.lucko.spark.api.statistic.types.DoubleStatistic;
+import me.lucko.spark.api.statistic.types.GenericStatistic;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+public abstract class AbstractStatistic<W extends Enum<W> & StatisticWindow> implements Statistic<W> {
+ private final String name;
+ protected final W[] windows;
+
+ protected AbstractStatistic(String name, Class<W> enumClass) {
+ this.name = name;
+ this.windows = enumClass.getEnumConstants();
+ }
+
+ @Override
+ public @NonNull String name() {
+ return this.name;
+ }
+
+ @Override
+ public W[] getWindows() {
+ return Arrays.copyOf(this.windows, this.windows.length);
+ }
+
+ public static abstract class Double<W extends Enum<W> & StatisticWindow> extends AbstractStatistic<W> implements DoubleStatistic<W> {
+ public Double(String name, Class<W> enumClass) {
+ super(name, enumClass);
+ }
+
+ @Override
+ public double[] poll() {
+ double[] values = new double[this.windows.length];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = poll(this.windows[i]);
+ }
+ return values;
+ }
+ }
+
+ public static abstract class Generic<T, W extends Enum<W> & StatisticWindow> extends AbstractStatistic<W> implements GenericStatistic<T, W> {
+ private final Class<T> typeClass;
+
+ public Generic(String name, Class<T> typeClass, Class<W> enumClass) {
+ super(name, enumClass);
+ this.typeClass = typeClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T[] poll() {
+ T[] values = (T[]) Array.newInstance(this.typeClass, this.windows.length);
+ for (int i = 0; i < values.length; i++) {
+ values[i] = poll(this.windows[i]);
+ }
+ return values;
+ }
+ }
+}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/api/GarbageCollectorInfo.java b/spark-common/src/main/java/me/lucko/spark/common/api/GarbageCollectorInfo.java
new file mode 100644
index 0000000..8d289aa
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/api/GarbageCollectorInfo.java
@@ -0,0 +1,69 @@
+/*
+ * 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.api;
+
+import me.lucko.spark.api.gc.GarbageCollector;
+import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+public class GarbageCollectorInfo implements GarbageCollector {
+ private final String name;
+ private final long totalCollections;
+ private final long totalTime;
+ private final double averageTime;
+ private final long averageFrequency;
+
+ public GarbageCollectorInfo(String name, GarbageCollectorStatistics stats, long serverUptime) {
+ this.name = name;
+ this.totalCollections = stats.getCollectionCount();
+ this.totalTime = stats.getCollectionTime();
+
+ double totalTimeDouble = this.totalTime;
+ this.averageTime = this.totalCollections == 0 ? 0 : totalTimeDouble / this.totalCollections;
+ this.averageFrequency = this.totalCollections == 0 ? 0 : (long) ((serverUptime - totalTimeDouble) / this.totalCollections);
+ }
+
+ @Override
+ public @NonNull String name() {
+ return this.name;
+ }
+
+ @Override
+ public long totalCollections() {
+ return this.totalCollections;
+ }
+
+ @Override
+ public long totalTime() {
+ return this.totalTime;
+ }
+
+ @Override
+ public double avgTime() {
+ return this.averageTime;
+ }
+
+ @Override
+ public long avgFrequency() {
+ return this.averageFrequency;
+ }
+}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java b/spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java
new file mode 100644
index 0000000..5ac41fc
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/api/SparkApi.java
@@ -0,0 +1,189 @@
+/*
+ * 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.api;
+
+import com.google.common.collect.ImmutableMap;
+
+import me.lucko.spark.api.Spark;
+import me.lucko.spark.api.SparkProvider;
+import me.lucko.spark.api.gc.GarbageCollector;
+import me.lucko.spark.api.statistic.misc.DoubleAverageInfo;
+import me.lucko.spark.api.statistic.types.DoubleStatistic;
+import me.lucko.spark.api.statistic.types.GenericStatistic;
+import me.lucko.spark.api.statistic.StatisticWindow;
+import me.lucko.spark.common.SparkPlatform;
+import me.lucko.spark.common.monitor.cpu.CpuMonitor;
+import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics;
+import me.lucko.spark.common.monitor.tick.TickStatistics;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Ref;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SparkApi implements Spark {
+ private static final Method SINGLETON_SET_METHOD;
+
+ static {
+ try {
+ SINGLETON_SET_METHOD = SparkProvider.class.getDeclaredMethod("set", Spark.class);
+ SINGLETON_SET_METHOD.setAccessible(true);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private final SparkPlatform platform;
+
+ public SparkApi(SparkPlatform platform) {
+ this.platform = platform;
+ }
+
+ @Override
+ public @NonNull DoubleStatistic<StatisticWindow.CpuUsage> cpuProcess() {
+ return new AbstractStatistic.Double<StatisticWindow.CpuUsage>(
+ "CPU Process Usage", StatisticWindow.CpuUsage.class
+ ) {
+ @Override
+ public double poll(StatisticWindow.@NonNull CpuUsage window) {
+ switch (window) {
+ case SECONDS_10:
+ return CpuMonitor.processLoad10SecAvg();
+ case MINUTES_1:
+ return CpuMonitor.processLoad1MinAvg();
+ case MINUTES_15:
+ return CpuMonitor.processLoad15MinAvg();
+ default:
+ throw new AssertionError(window);
+ }
+ }
+ };
+ }
+
+ @Override
+ public @NonNull DoubleStatistic<StatisticWindow.CpuUsage> cpuSystem() {
+ return new AbstractStatistic.Double<StatisticWindow.CpuUsage>(
+ "CPU System Usage", StatisticWindow.CpuUsage.class
+ ) {
+ @Override
+ public double poll(StatisticWindow.@NonNull CpuUsage window) {
+ switch (window) {
+ case SECONDS_10:
+ return CpuMonitor.systemLoad10SecAvg();
+ case MINUTES_1:
+ return CpuMonitor.systemLoad1MinAvg();
+ case MINUTES_15:
+ return CpuMonitor.systemLoad15MinAvg();
+ default:
+ throw new AssertionError(window);
+ }
+ }
+ };
+ }
+
+ @Override
+ public @Nullable DoubleStatistic<StatisticWindow.TicksPerSecond> tps() {
+ TickStatistics stats = this.platform.getTickStatistics();
+ if (stats == null) {
+ return null;
+ }
+
+ return new AbstractStatistic.Double<StatisticWindow.TicksPerSecond>(
+ "Ticks Per Second", StatisticWindow.TicksPerSecond.class
+ ) {
+ @Override
+ public double poll(StatisticWindow.@NonNull TicksPerSecond window) {
+ switch (window) {
+ case SECONDS_5:
+ return stats.tps5Sec();
+ case SECONDS_10:
+ return stats.tps10Sec();
+ case MINUTES_1:
+ return stats.tps1Min();
+ case MINUTES_5:
+ return stats.tps5Min();
+ case MINUTES_15:
+ return stats.tps15Min();
+ default:
+ throw new AssertionError(window);
+ }
+ }
+ };
+ }
+
+ @Override
+ public @Nullable GenericStatistic<DoubleAverageInfo, StatisticWindow.MillisPerTick> mspt() {
+ TickStatistics stats = this.platform.getTickStatistics();
+ if (stats == null || !stats.isDurationSupported()) {
+ return null;
+ }
+
+ return new AbstractStatistic.Generic<DoubleAverageInfo, StatisticWindow.MillisPerTick>(
+ "Milliseconds Per Tick", DoubleAverageInfo.class, StatisticWindow.MillisPerTick.class
+ ) {
+ @Override
+ public DoubleAverageInfo poll(StatisticWindow.@NonNull MillisPerTick window) {
+ switch (window) {
+ case SECONDS_10:
+ return stats.duration10Sec();
+ case MINUTES_1:
+ return stats.duration1Min();
+ default:
+ throw new AssertionError(window);
+ }
+ }
+ };
+ }
+
+ @Override
+ public @NonNull Map<String, GarbageCollector> gc() {
+ long serverUptime = System.currentTimeMillis() - this.platform.getServerNormalOperationStartTime();
+ Map<String, GarbageCollectorStatistics> stats = GarbageCollectorStatistics.pollStatsSubtractInitial(
+ this.platform.getStartupGcStatistics()
+ );
+
+ Map<String, GarbageCollector> map = new HashMap<>(stats.size());
+ for (Map.Entry<String, GarbageCollectorStatistics> entry : stats.entrySet()) {
+ map.put(entry.getKey(), new GarbageCollectorInfo(entry.getKey(), entry.getValue(), serverUptime));
+ }
+ return ImmutableMap.copyOf(map);
+ }
+
+ public static void register(Spark spark) {
+ try {
+ SINGLETON_SET_METHOD.invoke(null, spark);
+ } catch (ReflectiveOperationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void unregister() {
+ try {
+ SINGLETON_SET_METHOD.invoke(null, new Object[]{null});
+ } catch (ReflectiveOperationException e) {
+ e.printStackTrace();
+ }
+ }
+}
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 547131c..d6c0e10 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
@@ -351,13 +351,13 @@ public class HealthModule implements CommandModule {
public static TextComponent formatTickDurations(RollingAverage average) {
return text()
- .append(formatTickDuration(average.getMin()))
+ .append(formatTickDuration(average.min()))
.append(text('/', GRAY))
- .append(formatTickDuration(average.getMedian()))
+ .append(formatTickDuration(average.median()))
.append(text('/', GRAY))
- .append(formatTickDuration(average.getPercentile(MSPT_95_PERCENTILE)))
+ .append(formatTickDuration(average.percentile(MSPT_95_PERCENTILE)))
.append(text('/', GRAY))
- .append(formatTickDuration(average.getMax()))
+ .append(formatTickDuration(average.max()))
.build();
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/cpu/CpuMonitor.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/cpu/CpuMonitor.java
index 9c75dde..43e1f90 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/monitor/cpu/CpuMonitor.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/cpu/CpuMonitor.java
@@ -97,15 +97,15 @@ public enum CpuMonitor {
}
public static double systemLoad10SecAvg() {
- return SYSTEM_AVERAGE_10_SEC.getAverage();
+ return SYSTEM_AVERAGE_10_SEC.mean();
}
public static double systemLoad1MinAvg() {
- return SYSTEM_AVERAGE_1_MIN.getAverage();
+ return SYSTEM_AVERAGE_1_MIN.mean();
}
public static double systemLoad15MinAvg() {
- return SYSTEM_AVERAGE_15_MIN.getAverage();
+ return SYSTEM_AVERAGE_15_MIN.mean();
}
/**
@@ -128,15 +128,15 @@ public enum CpuMonitor {
}
public static double processLoad10SecAvg() {
- return PROCESS_AVERAGE_10_SEC.getAverage();
+ return PROCESS_AVERAGE_10_SEC.mean();
}
public static double processLoad1MinAvg() {
- return PROCESS_AVERAGE_1_MIN.getAverage();
+ return PROCESS_AVERAGE_1_MIN.mean();
}
public static double processLoad15MinAvg() {
- return PROCESS_AVERAGE_15_MIN.getAverage();
+ return PROCESS_AVERAGE_15_MIN.mean();
}
/**
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 2c6219a..87c41a4 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
@@ -20,13 +20,15 @@
package me.lucko.spark.common.util;
+import me.lucko.spark.api.statistic.misc.DoubleAverageInfo;
+
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
-public class RollingAverage {
+public class RollingAverage implements DoubleAverageInfo {
private final Queue<BigDecimal> samples;
private final int windowSize;
@@ -47,7 +49,8 @@ public class RollingAverage {
}
}
- public double getAverage() {
+ @Override
+ public double mean() {
synchronized (this) {
if (this.samples.isEmpty()) {
return 0;
@@ -57,7 +60,8 @@ public class RollingAverage {
}
}
- public double getMax() {
+ @Override
+ public double max() {
synchronized (this) {
BigDecimal max = null;
for (BigDecimal sample : this.samples) {
@@ -69,7 +73,8 @@ public class RollingAverage {
}
}
- public double getMin() {
+ @Override
+ public double min() {
synchronized (this) {
BigDecimal min = null;
for (BigDecimal sample : this.samples) {
@@ -81,11 +86,8 @@ public class RollingAverage {
}
}
- public double getMedian() {
- return getPercentile(0.50d);
- }
-
- public double getPercentile(double percentile) {
+ @Override
+ public double percentile(double percentile) {
if (percentile < 0 || percentile > 1) {
throw new IllegalArgumentException("Invalid percentile " + percentile);
}
diff --git a/spark-nukkit/src/main/java/me/lucko/spark/nukkit/NukkitSparkPlugin.java b/spark-nukkit/src/main/java/me/lucko/spark/nukkit/NukkitSparkPlugin.java
index eb783fa..9606614 100644
--- a/spark-nukkit/src/main/java/me/lucko/spark/nukkit/NukkitSparkPlugin.java
+++ b/spark-nukkit/src/main/java/me/lucko/spark/nukkit/NukkitSparkPlugin.java
@@ -20,6 +20,7 @@
package me.lucko.spark.nukkit;
+import me.lucko.spark.api.Spark;
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.SparkPlugin;
import me.lucko.spark.common.platform.PlatformInfo;
@@ -27,6 +28,7 @@ import me.lucko.spark.common.platform.PlatformInfo;
import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
import cn.nukkit.plugin.PluginBase;
+import cn.nukkit.plugin.service.ServicePriority;
import cn.nukkit.scheduler.AsyncTask;
import java.nio.file.Path;
@@ -89,4 +91,9 @@ public class NukkitSparkPlugin extends PluginBase implements SparkPlugin {
public PlatformInfo getPlatformInfo() {
return new NukkitPlatformInfo(getServer());
}
+
+ @Override
+ public void registerApi(Spark api) {
+ getServer().getServiceManager().register(Spark.class, api, this, ServicePriority.NORMAL);
+ }
}
diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java
index dbd0193..bfd0c3f 100644
--- a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java
+++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java
@@ -22,6 +22,7 @@ package me.lucko.spark.sponge;
import com.google.inject.Inject;
+import me.lucko.spark.api.Spark;
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.SparkPlugin;
import me.lucko.spark.common.platform.PlatformInfo;
@@ -140,6 +141,11 @@ public class SpongeSparkPlugin implements SparkPlugin {
return new SpongePlatformInfo(this.game);
}
+ @Override
+ public void registerApi(Spark api) {
+ this.game.getServiceManager().setProvider(this, Spark.class, api);
+ }
+
private static final class SparkCommand implements CommandCallable {
private final SpongeSparkPlugin plugin;