diff options
author | Luck <git@lucko.me> | 2021-04-07 11:51:03 +0100 |
---|---|---|
committer | Luck <git@lucko.me> | 2021-04-07 11:51:03 +0100 |
commit | 9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5 (patch) | |
tree | a2e153c4d723216f95c49153d5afef524dea9531 | |
parent | bb3046600f1d1a98a69f918dd59dff64e051480b (diff) | |
download | spark-9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5.tar.gz spark-9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5.tar.bz2 spark-9228cf8b55dc34a5f49bddfe4637fbd4dd7fecb5.zip |
Add a basic API for retrieving spark statistics
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; |