aboutsummaryrefslogtreecommitdiff
path: root/spark-common
diff options
context:
space:
mode:
Diffstat (limited to 'spark-common')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java19
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java4
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/activitylog/Activity.java111
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/activitylog/ActivityLog.java86
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java14
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/Command.java1
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java10
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java16
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java36
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java510
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapAnalysisModule.java292
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java365
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java21
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/sender/CommandSender.java2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDump.java24
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDumpSummary.java1
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectionMonitor.java43
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java14
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/tick/ReportPredicate.java90
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java236
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java4
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java1
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncSampler.java16
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/ClassRef.java25
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Element.java23
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrClass.java49
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrField.java31
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrReader.java97
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/MethodRef.java29
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Sample.java36
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/StackTrace.java28
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/java/JavaSampler.java3
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/java/TickedDataAggregator.java2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/node/StackTraceNode.java1
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/tick/AbstractTickHook.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickHook.java)2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/tick/AbstractTickReporter.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java)2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/tick/TickHook.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickHook.java)2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/tick/TickReporter.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java)2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/util/AbstractHttpClient.java1
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java59
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/util/MethodDisambiguator.java2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/util/RollingAverage.java2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/util/ThreadFinder.java3
44 files changed, 1171 insertions, 1146 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 0cc2144..a5dadba 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
@@ -22,6 +22,7 @@ package me.lucko.spark.common;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+
import me.lucko.spark.common.activitylog.ActivityLog;
import me.lucko.spark.common.command.Arguments;
import me.lucko.spark.common.command.Command;
@@ -39,10 +40,12 @@ import me.lucko.spark.common.command.tabcomplete.TabCompleter;
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 me.lucko.spark.common.sampler.tick.TickHook;
-import me.lucko.spark.common.sampler.tick.TickReporter;
+import me.lucko.spark.common.tick.TickHook;
+import me.lucko.spark.common.tick.TickReporter;
import me.lucko.spark.common.util.BytebinClient;
+
import net.kyori.adventure.text.event.ClickEvent;
+
import okhttp3.OkHttpClient;
import java.util.ArrayList;
@@ -52,9 +55,15 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
+import static net.kyori.adventure.text.format.TextDecoration.UNDERLINED;
/**
* Abstract spark implementation used by all platforms.
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 8d18b54..171367e 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
@@ -23,8 +23,8 @@ package me.lucko.spark.common;
import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.platform.PlatformInfo;
import me.lucko.spark.common.sampler.ThreadDumper;
-import me.lucko.spark.common.sampler.tick.TickHook;
-import me.lucko.spark.common.sampler.tick.TickReporter;
+import me.lucko.spark.common.tick.TickHook;
+import me.lucko.spark.common.tick.TickReporter;
import java.nio.file.Path;
import java.util.stream.Stream;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/activitylog/Activity.java b/spark-common/src/main/java/me/lucko/spark/common/activitylog/Activity.java
new file mode 100644
index 0000000..561515a
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/activitylog/Activity.java
@@ -0,0 +1,111 @@
+/*
+ * 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.activitylog;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+import me.lucko.spark.common.command.sender.CommandSender;
+
+import java.util.concurrent.TimeUnit;
+
+public final class Activity {
+ private final CommandSender.Data user;
+ private final long time;
+ private final String type;
+
+ private final String dataType;
+ private final String dataValue;
+
+ public static Activity urlActivity(CommandSender user, long time, String type, String url) {
+ return new Activity(user.toData(), time, type, "url", url);
+ }
+
+ public static Activity fileActivity(CommandSender user, long time, String type, String filePath) {
+ return new Activity(user.toData(), time, type, "file", filePath);
+ }
+
+ private Activity(CommandSender.Data user, long time, String type, String dataType, String dataValue) {
+ this.user = user;
+ this.time = time;
+ this.type = type;
+ this.dataType = dataType;
+ this.dataValue = dataValue;
+ }
+
+ public CommandSender.Data getUser() {
+ return this.user;
+ }
+
+ public long getTime() {
+ return this.time;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public String getDataType() {
+ return this.dataType;
+ }
+
+ public String getDataValue() {
+ return this.dataValue;
+ }
+
+ public boolean shouldExpire() {
+ if (this.dataType.equals("url")) {
+ return (System.currentTimeMillis() - this.time) > TimeUnit.DAYS.toMillis(60);
+ } else {
+ return false;
+ }
+ }
+
+ public JsonObject serialize() {
+ JsonObject object = new JsonObject();
+
+ object.add("user", this.user.serialize());
+ object.add("time", new JsonPrimitive(this.time));
+ object.add("type", new JsonPrimitive(this.type));
+
+ JsonObject data = new JsonObject();
+ data.add("type", new JsonPrimitive(this.dataType));
+ data.add("value", new JsonPrimitive(this.dataValue));
+ object.add("data", data);
+
+ return object;
+ }
+
+ public static Activity deserialize(JsonElement element) {
+ JsonObject object = element.getAsJsonObject();
+
+ CommandSender.Data user = CommandSender.Data.deserialize(object.get("user"));
+ long time = object.get("time").getAsJsonPrimitive().getAsLong();
+ String type = object.get("type").getAsJsonPrimitive().getAsString();
+
+ JsonObject dataObject = object.get("data").getAsJsonObject();
+ String dataType = dataObject.get("type").getAsJsonPrimitive().getAsString();
+ String dataValue = dataObject.get("value").getAsJsonPrimitive().getAsString();
+
+ return new Activity(user, time, type, dataType, dataValue);
+ }
+}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/activitylog/ActivityLog.java b/spark-common/src/main/java/me/lucko/spark/common/activitylog/ActivityLog.java
index b344ded..2693962 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/activitylog/ActivityLog.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/activitylog/ActivityLog.java
@@ -24,10 +24,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
-import me.lucko.spark.common.command.sender.CommandSender;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -37,7 +34,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.TimeUnit;
public class ActivityLog {
@@ -132,86 +128,4 @@ public class ActivityLog {
}
}
- public static final class Activity {
- private final CommandSender.Data user;
- private final long time;
- private final String type;
-
- private final String dataType;
- private final String dataValue;
-
- public static Activity urlActivity(CommandSender user, long time, String type, String url) {
- return new Activity(user.toData(), time, type, "url", url);
- }
-
- public static Activity fileActivity(CommandSender user, long time, String type, String filePath) {
- return new Activity(user.toData(), time, type, "file", filePath);
- }
-
- private Activity(CommandSender.Data user, long time, String type, String dataType, String dataValue) {
- this.user = user;
- this.time = time;
- this.type = type;
- this.dataType = dataType;
- this.dataValue = dataValue;
- }
-
- public CommandSender.Data getUser() {
- return this.user;
- }
-
- public long getTime() {
- return this.time;
- }
-
- public String getType() {
- return this.type;
- }
-
- public String getDataType() {
- return this.dataType;
- }
-
- public String getDataValue() {
- return this.dataValue;
- }
-
- public boolean shouldExpire() {
- if (this.dataType.equals("url")) {
- return (System.currentTimeMillis() - this.time) > TimeUnit.DAYS.toMillis(60);
- } else {
- return false;
- }
- }
-
- public JsonObject serialize() {
- JsonObject object = new JsonObject();
-
- object.add("user", this.user.serialize());
- object.add("time", new JsonPrimitive(this.time));
- object.add("type", new JsonPrimitive(this.type));
-
- JsonObject data = new JsonObject();
- data.add("type", new JsonPrimitive(this.dataType));
- data.add("value", new JsonPrimitive(this.dataValue));
- object.add("data", data);
-
- return object;
- }
-
- public static Activity deserialize(JsonElement element) {
- JsonObject object = element.getAsJsonObject();
-
- CommandSender.Data user = CommandSender.Data.deserialize(object.get("user"));
- long time = object.get("time").getAsJsonPrimitive().getAsLong();
- String type = object.get("type").getAsJsonPrimitive().getAsString();
-
- JsonObject dataObject = object.get("data").getAsJsonObject();
- String dataType = dataObject.get("type").getAsJsonPrimitive().getAsString();
- String dataValue = dataObject.get("value").getAsJsonPrimitive().getAsString();
-
- return new Activity(user, time, type, dataType, dataValue);
- }
- }
-
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
index 3cd0365..17c49e2 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
@@ -30,6 +30,9 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+/**
+ * Utility for parsing command-flag like arguments from raw space split strings.
+ */
public class Arguments {
private static final Pattern FLAG_REGEX = Pattern.compile("^--(.+)$");
@@ -110,19 +113,8 @@ public class Arguments {
}
public static final class ParseException extends IllegalArgumentException {
- public ParseException() {
- }
-
public ParseException(String s) {
super(s);
}
-
- public ParseException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public ParseException(Throwable cause) {
- super(cause);
- }
}
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
index e1a5146..dad15e6 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java
@@ -21,6 +21,7 @@
package me.lucko.spark.common.command;
import com.google.common.collect.ImmutableList;
+
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.sender.CommandSender;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java b/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java
index 874939e..1acb3dc 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java
@@ -22,6 +22,7 @@ package me.lucko.spark.common.command;
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.sender.CommandSender;
+
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
@@ -29,9 +30,11 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class CommandResponseHandler {
@@ -94,5 +97,4 @@ public class CommandResponseHandler {
return PREFIX.append(message);
}
-
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java
index ae4613a..2bdb5d6 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java
@@ -20,10 +20,11 @@
package me.lucko.spark.common.command.modules;
-import me.lucko.spark.common.activitylog.ActivityLog.Activity;
+import me.lucko.spark.common.activitylog.Activity;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
import me.lucko.spark.common.command.tabcomplete.TabCompleter;
+
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
@@ -36,10 +37,15 @@ import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
-import static me.lucko.spark.common.command.CommandResponseHandler.*;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static me.lucko.spark.common.command.CommandResponseHandler.applyPrefix;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class ActivityLogModule implements CommandModule, RowRenderer<Activity> {
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
index d66a181..8e2d199 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/GcMonitoringModule.java
@@ -21,6 +21,7 @@
package me.lucko.spark.common.command.modules;
import com.sun.management.GarbageCollectionNotificationInfo;
+
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
@@ -28,6 +29,7 @@ import me.lucko.spark.common.command.CommandResponseHandler;
import me.lucko.spark.common.monitor.memory.GarbageCollectionMonitor;
import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics;
import me.lucko.spark.common.util.FormatUtil;
+
import net.kyori.adventure.text.Component;
import java.lang.management.MemoryUsage;
@@ -37,12 +39,18 @@ import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static net.kyori.adventure.text.Component.empty;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class GcMonitoringModule implements CommandModule {
- private static final DecimalFormat df = new DecimalFormat("#.##");
+ private static final DecimalFormat DF = new DecimalFormat("#.##");
/** The gc monitoring instance currently running, if any */
private ReportingGcMonitor activeGcMonitor = null;
@@ -106,7 +114,7 @@ public class GcMonitoringModule implements CommandModule {
);
report.add(text()
.content(" ")
- .append(text(df.format(averageCollectionTime), GOLD))
+ .append(text(DF.format(averageCollectionTime), GOLD))
.append(text(" ms avg", GRAY))
.append(text(", ", DARK_GRAY))
.append(text(collectionCount, WHITE))
@@ -147,11 +155,10 @@ public class GcMonitoringModule implements CommandModule {
private static String formatTime(long millis) {
if (millis <= 0) {
- return "0ms";
+ return "0s";
}
long second = millis / 1000;
- //millis = millis % 1000;
long minute = second / 60;
second = second % 60;
@@ -162,9 +169,6 @@ public class GcMonitoringModule implements CommandModule {
if (second != 0) {
sb.append(second).append("s ");
}
- //if (millis != 0) {
- // sb.append(millis).append("ms");
- //}
return sb.toString().trim();
}
@@ -185,15 +189,7 @@ public class GcMonitoringModule implements CommandModule {
@Override
public void onGc(GarbageCollectionNotificationInfo data) {
- String gcType;
- if (data.getGcAction().equals("end of minor GC")) {
- gcType = "Young Gen";
- } else if (data.getGcAction().equals("end of major GC")) {
- gcType = "Old Gen";
- } else {
- gcType = data.getGcAction();
- }
-
+ String gcType = GarbageCollectionMonitor.getGcType(data);
String gcCause = data.getGcCause() != null ? " (cause = " + data.getGcCause() + ")" : "";
Map<String, MemoryUsage> beforeUsages = data.getGcInfo().getMemoryUsageBeforeGc();
@@ -207,7 +203,7 @@ public class GcMonitoringModule implements CommandModule {
.append(text(gcType + " "))
.append(text("GC", RED))
.append(text(" lasting "))
- .append(text(df.format(data.getGcInfo().getDuration()), GOLD))
+ .append(text(DF.format(data.getGcInfo().getDuration()), GOLD))
.append(text(" ms." + gcCause))
.build()
));
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 409eb38..c8f25c7 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
@@ -21,13 +21,19 @@
package me.lucko.spark.common.command.modules;
import com.google.common.base.Strings;
+
+import me.lucko.spark.common.SparkPlatform;
+import me.lucko.spark.common.command.Arguments;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
+import me.lucko.spark.common.command.CommandResponseHandler;
+import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.command.tabcomplete.TabCompleter;
import me.lucko.spark.common.monitor.cpu.CpuMonitor;
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.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.TextColor;
@@ -45,9 +51,17 @@ import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
-import static net.kyori.adventure.text.format.TextDecoration.*;
+import static net.kyori.adventure.text.Component.empty;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public class HealthModule implements CommandModule {
@@ -57,51 +71,7 @@ public class HealthModule implements CommandModule {
public void registerCommands(Consumer<Command> consumer) {
consumer.accept(Command.builder()
.aliases("tps", "cpu")
- .executor((platform, sender, resp, arguments) -> {
- TickStatistics tickStatistics = platform.getTickStatistics();
- if (tickStatistics != null) {
- resp.replyPrefixed(text("TPS from last 5s, 10s, 1m, 5m, 15m:"));
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps15Min()))
- .build()
- );
- resp.replyPrefixed(empty());
-
- if (tickStatistics.isDurationSupported()) {
- resp.replyPrefixed(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:"));
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
- .append(formatTickDurations(tickStatistics.duration1Min()))
- .build()
- );
- resp.replyPrefixed(empty());
- }
- }
-
- resp.replyPrefixed(text("CPU usage from last 10s, 1m, 15m:"));
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
- .append(text(" (system)", DARK_GRAY))
- .build()
- );
- resp.replyPrefixed(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
- .append(text(" (process)", DARK_GRAY))
- .build()
- );
- })
+ .executor(HealthModule::tps)
.tabCompleter(Command.TabCompleter.empty())
.build()
);
@@ -109,197 +79,265 @@ public class HealthModule implements CommandModule {
consumer.accept(Command.builder()
.aliases("healthreport", "health", "ht")
.argumentUsage("memory", null)
- .executor((platform, sender, resp, arguments) -> {
- resp.replyPrefixed(text("Generating server health report..."));
- platform.getPlugin().executeAsync(() -> {
- List<Component> report = new LinkedList<>();
- report.add(empty());
-
- TickStatistics tickStatistics = platform.getTickStatistics();
- if (tickStatistics != null) {
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("TPS from last 5s, 10s, 1m, 5m, 15m:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
- .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
- .append(formatTps(tickStatistics.tps15Min()))
- .build()
- );
- report.add(empty());
-
- if (tickStatistics.isDurationSupported()) {
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
- .append(formatTickDurations(tickStatistics.duration1Min()))
- .build()
- );
- report.add(empty());
- }
- }
-
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("CPU usage from last 10s, 1m, 15m:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
- .append(text(" (system)", DARK_GRAY))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
- .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
- .append(text(" (process)", DARK_GRAY))
- .build()
- );
- report.add(empty());
-
- MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
- MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Memory usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(heapUsage.getUsed()), WHITE))
- .append(space())
- .append(text("/", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(heapUsage.getMax()), WHITE))
- .append(text(" "))
- .append(text("(", GRAY))
- .append(text(FormatUtil.percent(heapUsage.getUsed(), heapUsage.getMax()), GREEN))
- .append(text(")", GRAY))
- .build()
- );
- report.add(text().content(" ").append(generateMemoryUsageDiagram(heapUsage, 40)).build());
- report.add(empty());
-
- if (arguments.boolFlag("memory")) {
- MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Non-heap memory usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(nonHeapUsage.getUsed()), WHITE))
- .build()
- );
- report.add(empty());
-
- List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
- for (MemoryPoolMXBean memoryPool : memoryPoolMXBeans) {
- if (memoryPool.getType() != MemoryType.HEAP) {
- continue;
- }
-
- MemoryUsage usage = memoryPool.getUsage();
- MemoryUsage collectionUsage = memoryPool.getCollectionUsage();
-
- if (usage.getMax() == -1) {
- usage = new MemoryUsage(usage.getInit(), usage.getUsed(), usage.getCommitted(), usage.getCommitted());
- }
-
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text(memoryPool.getName() + " pool usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(usage.getUsed()), WHITE))
- .append(space())
- .append(text("/", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(usage.getMax()), WHITE))
- .append(text(" "))
- .append(text("(", GRAY))
- .append(text(FormatUtil.percent(usage.getUsed(), usage.getMax()), GREEN))
- .append(text(")", GRAY))
- .build()
- );
- report.add(text().content(" ").append(generateMemoryPoolDiagram(usage, collectionUsage, 40)).build());
-
- if (collectionUsage != null) {
- report.add(text()
- .content(" ")
- .append(text("-", RED))
- .append(space())
- .append(text("Usage at last GC:", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(collectionUsage.getUsed()), WHITE))
- .build()
- );
- }
- report.add(empty());
- }
- }
-
- try {
- FileStore fileStore = Files.getFileStore(Paths.get("."));
- long totalSpace = fileStore.getTotalSpace();
- long usedSpace = totalSpace - fileStore.getUsableSpace();
- report.add(text()
- .append(text(">", DARK_GRAY, BOLD))
- .append(space())
- .append(text("Disk usage:", GOLD))
- .build()
- );
- report.add(text()
- .content(" ")
- .append(text(FormatUtil.formatBytes(usedSpace), WHITE))
- .append(space())
- .append(text("/", GRAY))
- .append(space())
- .append(text(FormatUtil.formatBytes(totalSpace), WHITE))
- .append(text(" "))
- .append(text("(", GRAY))
- .append(text(FormatUtil.percent(usedSpace, totalSpace), GREEN))
- .append(text(")", GRAY))
- .build()
- );
- report.add(text().content(" ").append(generateDiskUsageDiagram(usedSpace, totalSpace, 40)).build());
- report.add(empty());
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- report.forEach(resp::reply);
- });
- })
+ .executor(HealthModule::healthReport)
.tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--memory"))
.build()
);
}
+ private static void tps(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ TickStatistics tickStatistics = platform.getTickStatistics();
+ if (tickStatistics != null) {
+ resp.replyPrefixed(text("TPS from last 5s, 10s, 1m, 5m, 15m:"));
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps15Min()))
+ .build()
+ );
+ resp.replyPrefixed(empty());
+
+ if (tickStatistics.isDurationSupported()) {
+ resp.replyPrefixed(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:"));
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
+ .append(formatTickDurations(tickStatistics.duration1Min()))
+ .build()
+ );
+ resp.replyPrefixed(empty());
+ }
+ }
+
+ resp.replyPrefixed(text("CPU usage from last 10s, 1m, 15m:"));
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
+ .append(text(" (system)", DARK_GRAY))
+ .build()
+ );
+ resp.replyPrefixed(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
+ .append(text(" (process)", DARK_GRAY))
+ .build()
+ );
+ }
+
+ private static void healthReport(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ resp.replyPrefixed(text("Generating server health report..."));
+ platform.getPlugin().executeAsync(() -> {
+ List<Component> report = new LinkedList<>();
+ report.add(empty());
+
+ TickStatistics tickStatistics = platform.getTickStatistics();
+ if (tickStatistics != null) {
+ addTickStats(report, tickStatistics);
+ }
+
+ addCpuStats(report);
+
+ MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
+ addBasicMemoryStats(report, memoryMXBean);
+
+ if (arguments.boolFlag("memory")) {
+ addDetailedMemoryStats(report, memoryMXBean);
+ }
+
+ try {
+ addDiskStats(report);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ report.forEach(resp::reply);
+ });
+ }
+
+ private static void addTickStats(List<Component> report, TickStatistics tickStatistics) {
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("TPS from last 5s, 10s, 1m, 5m, 15m:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatTps(tickStatistics.tps5Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps10Sec())).append(text(", "))
+ .append(formatTps(tickStatistics.tps1Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps5Min())).append(text(", "))
+ .append(formatTps(tickStatistics.tps15Min()))
+ .build()
+ );
+ report.add(empty());
+
+ if (tickStatistics.isDurationSupported()) {
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("Tick durations (min/med/95%ile/max ms) from last 10s, 1m:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatTickDurations(tickStatistics.duration10Sec())).append(text("; "))
+ .append(formatTickDurations(tickStatistics.duration1Min()))
+ .build()
+ );
+ report.add(empty());
+ }
+ }
+
+ private static void addCpuStats(List<Component> report) {
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("CPU usage from last 10s, 1m, 15m:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.systemLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.systemLoad15MinAvg()))
+ .append(text(" (system)", DARK_GRAY))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(formatCpuUsage(CpuMonitor.processLoad10SecAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad1MinAvg())).append(text(", "))
+ .append(formatCpuUsage(CpuMonitor.processLoad15MinAvg()))
+ .append(text(" (process)", DARK_GRAY))
+ .build()
+ );
+ report.add(empty());
+ }
+
+ private static void addBasicMemoryStats(List<Component> report, MemoryMXBean memoryMXBean) {
+ MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("Memory usage:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(text(FormatUtil.formatBytes(heapUsage.getUsed()), WHITE))
+ .append(space())
+ .append(text("/", GRAY))
+ .append(space())
+ .append(text(FormatUtil.formatBytes(heapUsage.getMax()), WHITE))
+ .append(text(" "))
+ .append(text("(", GRAY))
+ .append(text(FormatUtil.percent(heapUsage.getUsed(), heapUsage.getMax()), GREEN))
+ .append(text(")", GRAY))
+ .build()
+ );
+ report.add(text().content(" ").append(generateMemoryUsageDiagram(heapUsage, 40)).build());
+ report.add(empty());
+ }
+
+ private static void addDetailedMemoryStats(List<Component> report, MemoryMXBean memoryMXBean) {
+ MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("Non-heap memory usage:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(text(FormatUtil.formatBytes(nonHeapUsage.getUsed()), WHITE))
+ .build()
+ );
+ report.add(empty());
+
+ List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
+ for (MemoryPoolMXBean memoryPool : memoryPoolMXBeans) {
+ if (memoryPool.getType() != MemoryType.HEAP) {
+ continue;
+ }
+
+ MemoryUsage usage = memoryPool.getUsage();
+ MemoryUsage collectionUsage = memoryPool.getCollectionUsage();
+
+ if (usage.getMax() == -1) {
+ usage = new MemoryUsage(usage.getInit(), usage.getUsed(), usage.getCommitted(), usage.getCommitted());
+ }
+
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text(memoryPool.getName() + " pool usage:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(text(FormatUtil.formatBytes(usage.getUsed()), WHITE))
+ .append(space())
+ .append(text("/", GRAY))
+ .append(space())
+ .append(text(FormatUtil.formatBytes(usage.getMax()), WHITE))
+ .append(text(" "))
+ .append(text("(", GRAY))
+ .append(text(FormatUtil.percent(usage.getUsed(), usage.getMax()), GREEN))
+ .append(text(")", GRAY))
+ .build()
+ );
+ report.add(text().content(" ").append(generateMemoryPoolDiagram(usage, collectionUsage, 40)).build());
+
+ if (collectionUsage != null) {
+ report.add(text()
+ .content(" ")
+ .append(text("-", RED))
+ .append(space())
+ .append(text("Usage at last GC:", GRAY))
+ .append(space())
+ .append(text(FormatUtil.formatBytes(collectionUsage.getUsed()), WHITE))
+ .build()
+ );
+ }
+ report.add(empty());
+ }
+ }
+
+ private static void addDiskStats(List<Component> report) throws IOException {
+ FileStore fileStore = Files.getFileStore(Paths.get("."));
+ long totalSpace = fileStore.getTotalSpace();
+ long usedSpace = totalSpace - fileStore.getUsableSpace();
+ report.add(text()
+ .append(text(">", DARK_GRAY, BOLD))
+ .append(space())
+ .append(text("Disk usage:", GOLD))
+ .build()
+ );
+ report.add(text()
+ .content(" ")
+ .append(text(FormatUtil.formatBytes(usedSpace), WHITE))
+ .append(space())
+ .append(text("/", GRAY))
+ .append(space())
+ .append(text(FormatUtil.formatBytes(totalSpace), WHITE))
+ .append(text(" "))
+ .append(text("(", GRAY))
+ .append(text(FormatUtil.percent(usedSpace, totalSpace), GREEN))
+ .append(text(")", GRAY))
+ .build()
+ );
+ report.add(text().content(" ").append(generateDiskUsageDiagram(usedSpace, totalSpace, 40)).build());
+ report.add(empty());
+ }
+
public static TextComponent formatTps(double tps) {
TextColor color;
if (tps > 18.0) {
@@ -313,7 +351,7 @@ public class HealthModule implements CommandModule {
return text((tps > 20.0 ? "*" : "") + Math.min(Math.round(tps * 100.0) / 100.0, 20.0), color);
}
- public static TextComponent formatTickDurations(RollingAverage average){
+ public static TextComponent formatTickDurations(RollingAverage average) {
return text()
.append(formatTickDuration(average.getMin()))
.append(text('/', GRAY))
@@ -325,7 +363,7 @@ public class HealthModule implements CommandModule {
.build();
}
- public static TextComponent formatTickDuration(double duration){
+ public static TextComponent formatTickDuration(double duration) {
TextColor color;
if (duration >= 50d) {
color = RED;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapAnalysisModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapAnalysisModule.java
index 39cb8a3..94e44a6 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapAnalysisModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HeapAnalysisModule.java
@@ -21,19 +21,25 @@
package me.lucko.spark.common.command.modules;
import me.lucko.spark.common.SparkPlatform;
-import me.lucko.spark.common.activitylog.ActivityLog.Activity;
+import me.lucko.spark.common.activitylog.Activity;
+import me.lucko.spark.common.command.Arguments;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
+import me.lucko.spark.common.command.CommandResponseHandler;
+import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.command.tabcomplete.TabCompleter;
import me.lucko.spark.common.heapdump.HeapDump;
import me.lucko.spark.common.heapdump.HeapDumpSummary;
import me.lucko.spark.common.util.FormatUtil;
+
import net.kyori.adventure.text.event.ClickEvent;
-import okhttp3.MediaType;
+
import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.LZMAOutputStream;
import org.tukaani.xz.XZOutputStream;
+import okhttp3.MediaType;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -48,8 +54,11 @@ import java.util.function.Consumer;
import java.util.function.LongConsumer;
import java.util.zip.GZIPOutputStream;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
public class HeapAnalysisModule implements CommandModule {
private static final MediaType SPARK_HEAP_MEDIA_TYPE = MediaType.parse("application/x-spark-heap");
@@ -59,44 +68,7 @@ public class HeapAnalysisModule implements CommandModule {
consumer.accept(Command.builder()
.aliases("heapsummary")
.argumentUsage("run-gc-before", null)
- .executor((platform, sender, resp, arguments) -> {
- platform.getPlugin().executeAsync(() -> {
- if (arguments.boolFlag("run-gc-before")) {
- resp.broadcastPrefixed(text("Running garbage collector..."));
- System.gc();
- }
-
- resp.broadcastPrefixed(text("Creating a new heap dump summary, please wait..."));
-
- HeapDumpSummary heapDump;
- try {
- heapDump = HeapDumpSummary.createNew();
- } catch (Exception e) {
- resp.broadcastPrefixed(text("An error occurred whilst inspecting the heap.", RED));
- e.printStackTrace();
- return;
- }
-
- byte[] output = heapDump.formCompressedDataPayload(platform.getPlugin().getPlatformInfo(), sender);
- try {
- String key = SparkPlatform.BYTEBIN_CLIENT.postContent(output, SPARK_HEAP_MEDIA_TYPE, false).key();
- String url = SparkPlatform.VIEWER_URL + key;
-
- resp.broadcastPrefixed(text("Heap dump summmary output:", GOLD));
- resp.broadcast(text()
- .content(url)
- .color(GRAY)
- .clickEvent(ClickEvent.openUrl(url))
- .build()
- );
-
- platform.getActivityLog().addToLog(Activity.urlActivity(sender, System.currentTimeMillis(), "Heap dump summary", url));
- } catch (IOException e) {
- resp.broadcastPrefixed(text("An error occurred whilst uploading the data.", RED));
- e.printStackTrace();
- }
- });
- })
+ .executor(HeapAnalysisModule::heapSummary)
.tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--run-gc-before"))
.build()
);
@@ -104,107 +76,153 @@ public class HeapAnalysisModule implements CommandModule {
consumer.accept(Command.builder()
.aliases("heapdump")
.argumentUsage("compress", "type")
- .executor((platform, sender, resp, arguments) -> {
- platform.getPlugin().executeAsync(() -> {
- Path pluginFolder = platform.getPlugin().getPluginDirectory();
- try {
- Files.createDirectories(pluginFolder);
- } catch (IOException e) {
- // ignore
- }
+ .executor(HeapAnalysisModule::heapDump)
+ .tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--compress", "--run-gc-before", "--include-non-live"))
+ .build()
+ );
+ }
- Path file = pluginFolder.resolve("heap-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + (HeapDump.isOpenJ9() ? ".phd" : ".hprof"));
- boolean liveOnly = !arguments.boolFlag("include-non-live");
+ private static void heapSummary(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ platform.getPlugin().executeAsync(() -> {
+ if (arguments.boolFlag("run-gc-before")) {
+ resp.broadcastPrefixed(text("Running garbage collector..."));
+ System.gc();
+ }
- if (arguments.boolFlag("run-gc-before")) {
- resp.broadcastPrefixed(text("Running garbage collector..."));
- System.gc();
- }
+ resp.broadcastPrefixed(text("Creating a new heap dump summary, please wait..."));
- resp.broadcastPrefixed(text("Creating a new heap dump, please wait..."));
+ HeapDumpSummary heapDump;
+ try {
+ heapDump = HeapDumpSummary.createNew();
+ } catch (Exception e) {
+ resp.broadcastPrefixed(text("An error occurred whilst inspecting the heap.", RED));
+ e.printStackTrace();
+ return;
+ }
- try {
- HeapDump.dumpHeap(file, liveOnly);
- } catch (Exception e) {
- resp.broadcastPrefixed(text("An error occurred whilst creating a heap dump.", RED));
- e.printStackTrace();
- return;
- }
+ byte[] output = heapDump.formCompressedDataPayload(platform.getPlugin().getPlatformInfo(), sender);
+ try {
+ String key = SparkPlatform.BYTEBIN_CLIENT.postContent(output, SPARK_HEAP_MEDIA_TYPE).key();
+ String url = SparkPlatform.VIEWER_URL + key;
- resp.broadcastPrefixed(text()
- .content("Heap dump written to: ")
- .color(GOLD)
- .append(text(file.toString(), GRAY))
- .build()
- );
- platform.getActivityLog().addToLog(Activity.fileActivity(sender, System.currentTimeMillis(), "Heap dump", file.toString()));
-
-
- CompressionMethod compress = null;
- Iterator<String> compressArgs = arguments.stringFlag("compress").iterator();
- if (compressArgs.hasNext()) {
- try {
- compress = CompressionMethod.valueOf(compressArgs.next().toUpperCase());
- } catch (IllegalArgumentException e) {
- // ignore
- }
- }
+ resp.broadcastPrefixed(text("Heap dump summmary output:", GOLD));
+ resp.broadcast(text()
+ .content(url)
+ .color(GRAY)
+ .clickEvent(ClickEvent.openUrl(url))
+ .build()
+ );
- if (compress != null) {
- resp.broadcastPrefixed(text("Compressing heap dump, please wait..."));
- try {
- long size = Files.size(file);
- AtomicLong lastReport = new AtomicLong(System.currentTimeMillis());
-
- LongConsumer progressHandler = progress -> {
- long timeSinceLastReport = System.currentTimeMillis() - lastReport.get();
- if (timeSinceLastReport > TimeUnit.SECONDS.toMillis(5)) {
- lastReport.set(System.currentTimeMillis());
-
- platform.getPlugin().executeAsync(() -> {
- resp.broadcastPrefixed(text()
- .color(GRAY)
- .append(text("Compressed "))
- .append(text(FormatUtil.formatBytes(progress), GOLD))
- .append(text(" / "))
- .append(text(FormatUtil.formatBytes(size), GOLD))
- .append(text(" so far... ("))
- .append(text(FormatUtil.percent(progress, size), GREEN))
- .append(text(")"))
- .build()
- );
- });
- }
- };
-
- Path compressedFile = compress.compress(file, progressHandler);
- long compressedSize = Files.size(compressedFile);
-
- resp.broadcastPrefixed(text()
- .color(GRAY)
- .append(text("Compression complete: "))
- .append(text(FormatUtil.formatBytes(size), GOLD))
- .append(text(" --> "))
- .append(text(FormatUtil.formatBytes(compressedSize), GOLD))
- .append(text(" ("))
- .append(text(FormatUtil.percent(compressedSize, size), GREEN))
- .append(text(")"))
- .build()
- );
-
- resp.broadcastPrefixed(text()
- .content("Compressed heap dump written to: ")
- .color(GOLD)
- .append(text(compressedFile.toString(), GRAY))
- .build()
- );
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- });
- })
- .tabCompleter((platform, sender, arguments) -> TabCompleter.completeForOpts(arguments, "--compress", "--run-gc-before", "--include-non-live"))
+ platform.getActivityLog().addToLog(Activity.urlActivity(sender, System.currentTimeMillis(), "Heap dump summary", url));
+ } catch (IOException e) {
+ resp.broadcastPrefixed(text("An error occurred whilst uploading the data.", RED));
+ e.printStackTrace();
+ }
+ });
+ }
+
+ private static void heapDump(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ platform.getPlugin().executeAsync(() -> {
+ Path pluginFolder = platform.getPlugin().getPluginDirectory();
+ try {
+ Files.createDirectories(pluginFolder);
+ } catch (IOException e) {
+ // ignore
+ }
+
+ Path file = pluginFolder.resolve("heap-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + (HeapDump.isOpenJ9() ? ".phd" : ".hprof"));
+ boolean liveOnly = !arguments.boolFlag("include-non-live");
+
+ if (arguments.boolFlag("run-gc-before")) {
+ resp.broadcastPrefixed(text("Running garbage collector..."));
+ System.gc();
+ }
+
+ resp.broadcastPrefixed(text("Creating a new heap dump, please wait..."));
+
+ try {
+ HeapDump.dumpHeap(file, liveOnly);
+ } catch (Exception e) {
+ resp.broadcastPrefixed(text("An error occurred whilst creating a heap dump.", RED));
+ e.printStackTrace();
+ return;
+ }
+
+ resp.broadcastPrefixed(text()
+ .content("Heap dump written to: ")
+ .color(GOLD)
+ .append(text(file.toString(), GRAY))
+ .build()
+ );
+ platform.getActivityLog().addToLog(Activity.fileActivity(sender, System.currentTimeMillis(), "Heap dump", file.toString()));
+
+
+ CompressionMethod compressionMethod = null;
+ Iterator<String> compressArgs = arguments.stringFlag("compress").iterator();
+ if (compressArgs.hasNext()) {
+ try {
+ compressionMethod = CompressionMethod.valueOf(compressArgs.next().toUpperCase());
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ }
+
+ if (compressionMethod != null) {
+ try {
+ heapDumpCompress(platform, resp, file, compressionMethod);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private static void heapDumpCompress(SparkPlatform platform, CommandResponseHandler resp, Path file, CompressionMethod method) throws IOException {
+ resp.broadcastPrefixed(text("Compressing heap dump, please wait..."));
+
+ long size = Files.size(file);
+ AtomicLong lastReport = new AtomicLong(System.currentTimeMillis());
+
+ LongConsumer progressHandler = progress -> {
+ long timeSinceLastReport = System.currentTimeMillis() - lastReport.get();
+ if (timeSinceLastReport > TimeUnit.SECONDS.toMillis(5)) {
+ lastReport.set(System.currentTimeMillis());
+
+ platform.getPlugin().executeAsync(() -> {
+ resp.broadcastPrefixed(text()
+ .color(GRAY)
+ .append(text("Compressed "))
+ .append(text(FormatUtil.formatBytes(progress), GOLD))
+ .append(text(" / "))
+ .append(text(FormatUtil.formatBytes(size), GOLD))
+ .append(text(" so far... ("))
+ .append(text(FormatUtil.percent(progress, size), GREEN))
+ .append(text(")"))
+ .build()
+ );
+ });
+ }
+ };
+
+ Path compressedFile = method.compress(file, progressHandler);
+ long compressedSize = Files.size(compressedFile);
+
+ resp.broadcastPrefixed(text()
+ .color(GRAY)
+ .append(text("Compression complete: "))
+ .append(text(FormatUtil.formatBytes(size), GOLD))
+ .append(text(" --> "))
+ .append(text(FormatUtil.formatBytes(compressedSize), GOLD))
+ .append(text(" ("))
+ .append(text(FormatUtil.percent(compressedSize, size), GREEN))
+ .append(text(")"))
+ .build()
+ );
+
+ resp.broadcastPrefixed(text()
+ .content("Compressed heap dump written to: ")
+ .color(GOLD)
+ .append(text(compressedFile.toString(), GRAY))
.build()
);
}
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 094b398..ff577d5 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
@@ -21,11 +21,14 @@
package me.lucko.spark.common.command.modules;
import com.google.common.collect.Iterables;
+
import me.lucko.spark.common.SparkPlatform;
-import me.lucko.spark.common.activitylog.ActivityLog.Activity;
+import me.lucko.spark.common.activitylog.Activity;
+import me.lucko.spark.common.command.Arguments;
import me.lucko.spark.common.command.Command;
import me.lucko.spark.common.command.CommandModule;
import me.lucko.spark.common.command.CommandResponseHandler;
+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.sampler.Sampler;
@@ -35,9 +38,11 @@ import me.lucko.spark.common.sampler.ThreadGrouper;
import me.lucko.spark.common.sampler.ThreadNodeOrder;
import me.lucko.spark.common.sampler.async.AsyncSampler;
import me.lucko.spark.common.sampler.node.MergeMode;
-import me.lucko.spark.common.sampler.tick.TickHook;
+import me.lucko.spark.common.tick.TickHook;
import me.lucko.spark.common.util.MethodDisambiguator;
+
import net.kyori.adventure.text.event.ClickEvent;
+
import okhttp3.MediaType;
import java.io.IOException;
@@ -50,8 +55,12 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
-import static net.kyori.adventure.text.Component.*;
-import static net.kyori.adventure.text.format.NamedTextColor.*;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
public class SamplerModule implements CommandModule {
private static final MediaType SPARK_SAMPLER_MEDIA_TYPE = MediaType.parse("application/x-spark-sampler");
@@ -84,170 +93,7 @@ public class SamplerModule implements CommandModule {
.argumentUsage("force-java-sampler", null)
.argumentUsage("stop --comment", "comment")
.argumentUsage("stop --order-by-time", null)
- .executor((platform, sender, resp, arguments) -> {
- if (arguments.boolFlag("info")) {
- if (this.activeSampler == null) {
- resp.replyPrefixed(text("There isn't an active profiler running."));
- } else {
- long timeout = this.activeSampler.getEndTime();
- if (timeout == -1) {
- resp.replyPrefixed(text("There is an active profiler currently running, with no defined timeout."));
- } else {
- long timeoutDiff = (timeout - System.currentTimeMillis()) / 1000L;
- resp.replyPrefixed(text("There is an active profiler currently running, due to timeout in " + timeoutDiff + " seconds."));
- }
-
- long runningTime = (System.currentTimeMillis() - this.activeSampler.getStartTime()) / 1000L;
- resp.replyPrefixed(text("It has been profiling for " + runningTime + " seconds so far."));
- }
- return;
- }
-
- if (arguments.boolFlag("cancel")) {
- if (this.activeSampler == null) {
- resp.replyPrefixed(text("There isn't an active profiler running."));
- } else {
- close();
- resp.broadcastPrefixed(text("The active profiler has been cancelled.", GOLD));
- }
- return;
- }
-
- if (arguments.boolFlag("stop") || arguments.boolFlag("upload")) {
- if (this.activeSampler == null) {
- resp.replyPrefixed(text("There isn't an active profiler running."));
- } else {
- this.activeSampler.stop();
- resp.broadcastPrefixed(text("The active profiler has been stopped! Uploading results..."));
- ThreadNodeOrder threadOrder = arguments.boolFlag("order-by-time") ? ThreadNodeOrder.BY_TIME : ThreadNodeOrder.BY_NAME;
- String comment = Iterables.getFirst(arguments.stringFlag("comment"), null);
- MethodDisambiguator methodDisambiguator = new MethodDisambiguator();
- MergeMode mergeMode = arguments.boolFlag("separate-parent-calls") ? MergeMode.separateParentCalls(methodDisambiguator) : MergeMode.sameMethod(methodDisambiguator);
- handleUpload(platform, resp, this.activeSampler, threadOrder, comment, mergeMode);
- this.activeSampler = null;
- }
- return;
- }
-
- int timeoutSeconds = arguments.intFlag("timeout");
- if (timeoutSeconds != -1 && timeoutSeconds <= 10) {
- resp.replyPrefixed(text("The specified timeout is not long enough for accurate results to be formed. " +
- "Please choose a value greater than 10.", RED));
- return;
- }
-
- if (timeoutSeconds != -1 && timeoutSeconds < 30) {
- resp.replyPrefixed(text("The accuracy of the output will significantly improve when the profiler is able to run for longer periods. " +
- "Consider setting a timeout value over 30 seconds."));
- }
-
- double intervalMillis = arguments.doubleFlag("interval");
- if (intervalMillis <= 0) {
- intervalMillis = 4;
- }
-
- boolean ignoreSleeping = arguments.boolFlag("ignore-sleeping");
- boolean ignoreNative = arguments.boolFlag("ignore-native");
- boolean forceJavaSampler = arguments.boolFlag("force-java-sampler");
-
- Set<String> threads = arguments.stringFlag("thread");
- ThreadDumper threadDumper;
- if (threads.isEmpty()) {
- // use the server thread
- threadDumper = platform.getPlugin().getDefaultThreadDumper();
- } else if (threads.contains("*")) {
- threadDumper = ThreadDumper.ALL;
- } else {
- if (arguments.boolFlag("regex")) {
- threadDumper = new ThreadDumper.Regex(threads);
- } else {
- // specific matches
- threadDumper = new ThreadDumper.Specific(threads);
- }
- }
-
- ThreadGrouper threadGrouper;
- if (arguments.boolFlag("combine-all")) {
- threadGrouper = ThreadGrouper.AS_ONE;
- } else if (arguments.boolFlag("not-combined")) {
- threadGrouper = ThreadGrouper.BY_NAME;
- } else {
- threadGrouper = ThreadGrouper.BY_POOL;
- }
-
- int ticksOver = arguments.intFlag("only-ticks-over");
- TickHook tickHook = null;
- if (ticksOver != -1) {
- tickHook = platform.getTickHook();
- if (tickHook == null) {
- resp.replyPrefixed(text("Tick counting is not supported!", RED));
- return;
- }
- }
-
- if (this.activeSampler != null) {
- resp.replyPrefixed(text("An active profiler is already running."));
- return;
- }
-
- resp.broadcastPrefixed(text("Initializing a new profiler, please wait..."));
-
- SamplerBuilder builder = new SamplerBuilder();
- builder.threadDumper(threadDumper);
- builder.threadGrouper(threadGrouper);
- if (timeoutSeconds != -1) {
- builder.completeAfter(timeoutSeconds, TimeUnit.SECONDS);
- }
- builder.samplingInterval(intervalMillis);
- builder.ignoreSleeping(ignoreSleeping);
- builder.ignoreNative(ignoreNative);
- builder.forceJavaSampler(forceJavaSampler);
- if (ticksOver != -1) {
- builder.ticksOver(ticksOver, tickHook);
- }
- Sampler sampler = this.activeSampler = builder.start();
-
- resp.broadcastPrefixed(text()
- .append(text("Profiler now active!", GOLD))
- .append(space())
- .append(text("(" + (sampler instanceof AsyncSampler ? "async" : "built-in java") + ")", DARK_GRAY))
- .build()
- );
- if (timeoutSeconds == -1) {
- resp.broadcastPrefixed(text("Use '/" + platform.getPlugin().getCommandName() + " profiler --stop' to stop profiling and upload the results."));
- } else {
- resp.broadcastPrefixed(text("The results will be automatically returned after the profiler has been running for " + timeoutSeconds + " seconds."));
- }
-
- CompletableFuture<? extends Sampler> future = this.activeSampler.getFuture();
-
- // send message if profiling fails
- future.whenCompleteAsync((s, throwable) -> {
- if (throwable != null) {
- resp.broadcastPrefixed(text("Profiler operation failed unexpectedly. Error: " + throwable.toString(), RED));
- throwable.printStackTrace();
- }
- });
-
- // set activeSampler to null when complete.
- future.whenCompleteAsync((s, throwable) -> {
- if (sampler == this.activeSampler) {
- this.activeSampler = null;
- }
- });
-
- // await the result
- if (timeoutSeconds != -1) {
- ThreadNodeOrder threadOrder = arguments.boolFlag("order-by-time") ? ThreadNodeOrder.BY_TIME : ThreadNodeOrder.BY_NAME;
- String comment = Iterables.getFirst(arguments.stringFlag("comment"), null);
- MethodDisambiguator methodDisambiguator = new MethodDisambiguator();
- MergeMode mergeMode = arguments.boolFlag("separate-parent-calls") ? MergeMode.separateParentCalls(methodDisambiguator) : MergeMode.sameMethod(methodDisambiguator);
- future.thenAcceptAsync(s -> {
- resp.broadcastPrefixed(text("The active profiler has completed! Uploading results..."));
- handleUpload(platform, resp, s, threadOrder, comment, mergeMode);
- });
- }
- })
+ .executor(this::profiler)
.tabCompleter((platform, sender, arguments) -> {
if (arguments.contains("--info") || arguments.contains("--cancel")) {
return Collections.emptyList();
@@ -271,11 +117,192 @@ public class SamplerModule implements CommandModule {
);
}
+ private void profiler(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ if (arguments.boolFlag("info")) {
+ profilerInfo(resp);
+ return;
+ }
+
+ if (arguments.boolFlag("cancel")) {
+ profilerCancel(resp);
+ return;
+ }
+
+ if (arguments.boolFlag("stop") || arguments.boolFlag("upload")) {
+ profilerStop(platform, sender, resp, arguments);
+ return;
+ }
+
+ profilerStart(platform, sender, resp, arguments);
+ }
+
+ private void profilerStart(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ int timeoutSeconds = arguments.intFlag("timeout");
+ if (timeoutSeconds != -1 && timeoutSeconds <= 10) {
+ resp.replyPrefixed(text("The specified timeout is not long enough for accurate results to be formed. " +
+ "Please choose a value greater than 10.", RED));
+ return;
+ }
+
+ if (timeoutSeconds != -1 && timeoutSeconds < 30) {
+ resp.replyPrefixed(text("The accuracy of the output will significantly improve when the profiler is able to run for longer periods. " +
+ "Consider setting a timeout value over 30 seconds."));
+ }
+
+ double intervalMillis = arguments.doubleFlag("interval");
+ if (intervalMillis <= 0) {
+ intervalMillis = 4;
+ }
+
+ boolean ignoreSleeping = arguments.boolFlag("ignore-sleeping");
+ boolean ignoreNative = arguments.boolFlag("ignore-native");
+ boolean forceJavaSampler = arguments.boolFlag("force-java-sampler");
+
+ Set<String> threads = arguments.stringFlag("thread");
+ ThreadDumper threadDumper;
+ if (threads.isEmpty()) {
+ // use the server thread
+ threadDumper = platform.getPlugin().getDefaultThreadDumper();
+ } else if (threads.contains("*")) {
+ threadDumper = ThreadDumper.ALL;
+ } else {
+ if (arguments.boolFlag("regex")) {
+ threadDumper = new ThreadDumper.Regex(threads);
+ } else {
+ // specific matches
+ threadDumper = new ThreadDumper.Specific(threads);
+ }
+ }
+
+ ThreadGrouper threadGrouper;
+ if (arguments.boolFlag("combine-all")) {
+ threadGrouper = ThreadGrouper.AS_ONE;
+ } else if (arguments.boolFlag("not-combined")) {
+ threadGrouper = ThreadGrouper.BY_NAME;
+ } else {
+ threadGrouper = ThreadGrouper.BY_POOL;
+ }
+
+ int ticksOver = arguments.intFlag("only-ticks-over");
+ TickHook tickHook = null;
+ if (ticksOver != -1) {
+ tickHook = platform.getTickHook();
+ if (tickHook == null) {
+ resp.replyPrefixed(text("Tick counting is not supported!", RED));
+ return;
+ }
+ }
+
+ if (this.activeSampler != null) {
+ resp.replyPrefixed(text("An active profiler is already running."));
+ return;
+ }
+
+ resp.broadcastPrefixed(text("Initializing a new profiler, please wait..."));
+
+ SamplerBuilder builder = new SamplerBuilder();
+ builder.threadDumper(threadDumper);
+ builder.threadGrouper(threadGrouper);
+ if (timeoutSeconds != -1) {
+ builder.completeAfter(timeoutSeconds, TimeUnit.SECONDS);
+ }
+ builder.samplingInterval(intervalMillis);
+ builder.ignoreSleeping(ignoreSleeping);
+ builder.ignoreNative(ignoreNative);
+ builder.forceJavaSampler(forceJavaSampler);
+ if (ticksOver != -1) {
+ builder.ticksOver(ticksOver, tickHook);
+ }
+ Sampler sampler = this.activeSampler = builder.start();
+
+ resp.broadcastPrefixed(text()
+ .append(text("Profiler now active!", GOLD))
+ .append(space())
+ .append(text("(" + (sampler instanceof AsyncSampler ? "async" : "built-in java") + ")", DARK_GRAY))
+ .build()
+ );
+ if (timeoutSeconds == -1) {
+ resp.broadcastPrefixed(text("Use '/" + platform.getPlugin().getCommandName() + " profiler --stop' to stop profiling and upload the results."));
+ } else {
+ resp.broadcastPrefixed(text("The results will be automatically returned after the profiler has been running for " + timeoutSeconds + " seconds."));
+ }
+
+ CompletableFuture<? extends Sampler> future = this.activeSampler.getFuture();
+
+ // send message if profiling fails
+ future.whenCompleteAsync((s, throwable) -> {
+ if (throwable != null) {
+ resp.broadcastPrefixed(text("Profiler operation failed unexpectedly. Error: " + throwable.toString(), RED));
+ throwable.printStackTrace();
+ }
+ });
+
+ // set activeSampler to null when complete.
+ future.whenCompleteAsync((s, throwable) -> {
+ if (sampler == this.activeSampler) {
+ this.activeSampler = null;
+ }
+ });
+
+ // await the result
+ if (timeoutSeconds != -1) {
+ ThreadNodeOrder threadOrder = arguments.boolFlag("order-by-time") ? ThreadNodeOrder.BY_TIME : ThreadNodeOrder.BY_NAME;
+ String comment = Iterables.getFirst(arguments.stringFlag("comment"), null);
+ MethodDisambiguator methodDisambiguator = new MethodDisambiguator();
+ MergeMode mergeMode = arguments.boolFlag("separate-parent-calls") ? MergeMode.separateParentCalls(methodDisambiguator) : MergeMode.sameMethod(methodDisambiguator);
+ future.thenAcceptAsync(s -> {
+ resp.broadcastPrefixed(text("The active profiler has completed! Uploading results..."));
+ handleUpload(platform, resp, s, threadOrder, comment, mergeMode);
+ });
+ }
+ }
+
+ private void profilerInfo(CommandResponseHandler resp) {
+ if (this.activeSampler == null) {
+ resp.replyPrefixed(text("There isn't an active profiler running."));
+ } else {
+ long timeout = this.activeSampler.getEndTime();
+ if (timeout == -1) {
+ resp.replyPrefixed(text("There is an active profiler currently running, with no defined timeout."));
+ } else {
+ long timeoutDiff = (timeout - System.currentTimeMillis()) / 1000L;
+ resp.replyPrefixed(text("There is an active profiler currently running, due to timeout in " + timeoutDiff + " seconds."));
+ }
+
+ long runningTime = (System.currentTimeMillis() - this.activeSampler.getStartTime()) / 1000L;
+ resp.replyPrefixed(text("It has been profiling for " + runningTime + " seconds so far."));
+ }
+ }
+
+ private void profilerCancel(CommandResponseHandler resp) {
+ if (this.activeSampler == null) {
+ resp.replyPrefixed(text("There isn't an active profiler running."));
+ } else {
+ close();
+ resp.broadcastPrefixed(text("The active profiler has been cancelled.", GOLD));
+ }
+ }
+
+ private void profilerStop(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) {
+ if (this.activeSampler == null) {
+ resp.replyPrefixed(text("There isn't an active profiler running."));
+ } else {
+ this.activeSampler.stop();
+ resp.broadcastPrefixed(text("The active profiler has been stopped! Uploading results..."));
+ ThreadNodeOrder threadOrder = arguments.boolFlag("order-by-time") ? ThreadNodeOrder.BY_TIME : ThreadNodeOrder.BY_NAME;
+ String comment = Iterables.getFirst(arguments.stringFlag("comment"), null);
+ MethodDisambiguator methodDisambiguator = new MethodDisambiguator();
+ MergeMode mergeMode = arguments.boolFlag("separate-parent-calls") ? MergeMode.separateParentCalls(methodDisambiguator) : MergeMode.sameMethod(methodDisambiguator);
+ handleUpload(platform, resp, this.activeSampler, threadOrder, comment, mergeMode);
+ this.activeSampler = null;
+ }
+ }
+
private void handleUpload(SparkPlatform platform, CommandResponseHandler resp, Sampler sampler, ThreadNodeOrder threadOrder, String comment, MergeMode mergeMode) {
platform.getPlugin().executeAsync(() -> {
byte[] output = sampler.formCompressedDataPayload(platform.getPlugin().getPlatformInfo(), resp.sender(), threadOrder, comment, mergeMode);
try {
- String key = SparkPlatform.BYTEBIN_CLIENT.postContent(output, SPARK_SAMPLER_MEDIA_TYPE, false).key();
+ String key = SparkPlatform.BYTEBIN_CLIENT.postContent(output, SPARK_SAMPLER_MEDIA_TYPE).key();
String url = SparkPlatform.VIEWER_URL + key;
resp.broadcastPrefixed(text("Profiler results:", GOLD));
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 043bc65..f5f4fce 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
@@ -25,26 +25,25 @@ import me.lucko.spark.common.command.Command;
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.ReportPredicate;
import me.lucko.spark.common.monitor.tick.TickMonitor;
-import me.lucko.spark.common.monitor.tick.TickMonitor.ReportPredicate;
-import me.lucko.spark.common.sampler.tick.TickHook;
+import me.lucko.spark.common.tick.TickHook;
+
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.function.Consumer;
-import static net.kyori.adventure.text.Component.*;
+import static net.kyori.adventure.text.Component.text;
public class TickMonitoringModule implements CommandModule {
/** The tick hook instance currently running, if any */
- private TickHook tickHook = null;
- private ReportingTickMonitor activeTickMonitor = null;
+ private TickMonitor activeTickMonitor = null;
@Override
public void close() {
if (this.activeTickMonitor != null) {
- this.tickHook.removeCallback(this.activeTickMonitor);
this.activeTickMonitor.close();
this.activeTickMonitor = null;
}
@@ -58,10 +57,8 @@ public class TickMonitoringModule implements CommandModule {
.argumentUsage("threshold-tick", "tick duration")
.argumentUsage("without-gc", null)
.executor((platform, sender, resp, arguments) -> {
- if (this.tickHook == null) {
- this.tickHook = platform.getTickHook();
- }
- if (this.tickHook == null) {
+ TickHook tickHook = platform.getTickHook();
+ if (tickHook == null) {
resp.replyPrefixed(text("Not supported!", NamedTextColor.RED));
return;
}
@@ -78,8 +75,8 @@ public class TickMonitoringModule implements CommandModule {
reportPredicate = new ReportPredicate.PercentageChangeGt(100);
}
- this.activeTickMonitor = new ReportingTickMonitor(platform, resp, this.tickHook, reportPredicate, !arguments.boolFlag("without-gc"));
- this.tickHook.addCallback(this.activeTickMonitor);
+ this.activeTickMonitor = new ReportingTickMonitor(platform, resp, tickHook, reportPredicate, !arguments.boolFlag("without-gc"));
+ this.activeTickMonitor.start();
} else {
close();
resp.broadcastPrefixed(text("Tick monitor disabled."));
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/sender/CommandSender.java b/spark-common/src/main/java/me/lucko/spark/common/command/sender/CommandSender.java
index b10c7d8..9feeac2 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/sender/CommandSender.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/sender/CommandSender.java
@@ -23,7 +23,9 @@ package me.lucko.spark.common.command.sender;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
+
import me.lucko.spark.proto.SparkProtos.CommandSenderData;
+
import net.kyori.adventure.text.Component;
import java.util.UUID;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDump.java b/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDump.java
index 975dbb3..955bafe 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDump.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDump.java
@@ -49,18 +49,26 @@ public enum HeapDump {
String outputPathString = outputPath.toAbsolutePath().normalize().toString();
if (isOpenJ9()) {
- Class<?> dumpClass = Class.forName("com.ibm.jvm.Dump");
- Method heapDumpMethod = dumpClass.getMethod("heapDumpToFile", String.class);
- heapDumpMethod.invoke(null, outputPathString);
+ dumpOpenJ9(outputPathString);
} else {
- MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
- ObjectName diagnosticBeanName = ObjectName.getInstance(DIAGNOSTIC_BEAN);
-
- HotSpotDiagnosticMXBean proxy = JMX.newMXBeanProxy(beanServer, diagnosticBeanName, HotSpotDiagnosticMXBean.class);
- proxy.dumpHeap(outputPathString, live);
+ dumpHotspot(outputPathString, live);
}
}
+ private static void dumpOpenJ9(String outputPathString) throws Exception {
+ Class<?> dumpClass = Class.forName("com.ibm.jvm.Dump");
+ Method heapDumpMethod = dumpClass.getMethod("heapDumpToFile", String.class);
+ heapDumpMethod.invoke(null, outputPathString);
+ }
+
+ private static void dumpHotspot(String outputPathString, boolean live) throws Exception {
+ MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
+ ObjectName diagnosticBeanName = ObjectName.getInstance(DIAGNOSTIC_BEAN);
+
+ HotSpotDiagnosticMXBean proxy = JMX.newMXBeanProxy(beanServer, diagnosticBeanName, HotSpotDiagnosticMXBean.class);
+ proxy.dumpHeap(outputPathString, live);
+ }
+
public static boolean isOpenJ9() {
try {
Class.forName("com.ibm.jvm.Dump");
diff --git a/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDumpSummary.java b/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDumpSummary.java
index 61ffd71..a1ad819 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDumpSummary.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/heapdump/HeapDumpSummary.java
@@ -25,6 +25,7 @@ import me.lucko.spark.common.platform.PlatformInfo;
import me.lucko.spark.proto.SparkProtos;
import me.lucko.spark.proto.SparkProtos.HeapData;
import me.lucko.spark.proto.SparkProtos.HeapEntry;
+
import org.objectweb.asm.Type;
import java.io.ByteArrayOutputStream;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectionMonitor.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectionMonitor.java
index f66d43b..9bff1e2 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectionMonitor.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectionMonitor.java
@@ -33,21 +33,28 @@ import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import javax.management.openmbean.CompositeData;
+/**
+ * Monitoring process for garbage collections.
+ */
public class GarbageCollectionMonitor implements NotificationListener, AutoCloseable {
+ /** The registered listeners */
private final List<Listener> listeners = new ArrayList<>();
+ /** A list of the NotificationEmitters that feed information to this monitor. */
private final List<NotificationEmitter> emitters = new ArrayList<>();
public GarbageCollectionMonitor() {
- List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();
- for (GarbageCollectorMXBean bean : beans) {
- if (!(bean instanceof NotificationEmitter)) {
- continue;
- }
+ // Add ourselves as a notification listener for all GarbageCollectorMXBean that
+ // support notifications.
+ for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
+ if (bean instanceof NotificationEmitter) {
+ NotificationEmitter notificationEmitter = (NotificationEmitter) bean;
+ notificationEmitter.addNotificationListener(this, null, null);
- NotificationEmitter notificationEmitter = (NotificationEmitter) bean;
- notificationEmitter.addNotificationListener(this, null, null);
- this.emitters.add(notificationEmitter);
+ // Keep track of the notification emitters we subscribe to so
+ // the listeners can be removed on #close
+ this.emitters.add(notificationEmitter);
+ }
}
}
@@ -61,6 +68,7 @@ public class GarbageCollectionMonitor implements NotificationListener, AutoClose
@Override
public void handleNotification(Notification notification, Object handback) {
+ // we're only interested in GC notifications
if (!notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
return;
}
@@ -84,8 +92,27 @@ public class GarbageCollectionMonitor implements NotificationListener, AutoClose
this.listeners.clear();
}
+ /**
+ * A simple listener object for garbage collections.
+ */
public interface Listener {
void onGc(GarbageCollectionNotificationInfo data);
}
+ /**
+ * Gets a human-friendly description for the type of the given GC notification.
+ *
+ * @param info the notification object
+ * @return the name of the GC type
+ */
+ public static String getGcType(GarbageCollectionNotificationInfo info) {
+ if (info.getGcAction().equals("end of minor GC")) {
+ return "Young Gen";
+ } else if (info.getGcAction().equals("end of major GC")) {
+ return "Old Gen";
+ } else {
+ return info.getGcAction();
+ }
+ }
+
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java
index e875fd7..c831ea1 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/memory/GarbageCollectorStatistics.java
@@ -32,6 +32,11 @@ import java.util.Map;
public class GarbageCollectorStatistics {
public static final GarbageCollectorStatistics ZERO = new GarbageCollectorStatistics(0, 0);
+ /**
+ * Polls a set of statistics from the {@link GarbageCollectorMXBean}.
+ *
+ * @return the polled statistics
+ */
public static Map<String, GarbageCollectorStatistics> pollStats() {
ImmutableMap.Builder<String, GarbageCollectorStatistics> stats = ImmutableMap.builder();
for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
@@ -40,6 +45,15 @@ public class GarbageCollectorStatistics {
return stats.build();
}
+ /**
+ * Polls a set of statistics from the {@link GarbageCollectorMXBean}, then subtracts
+ * {@code initial} from them.
+ *
+ * <p>The reason for subtracting the initial statistics is to ignore GC activity
+ * that took place before the server/client fully started.</p>
+ *
+ * @return the polled statistics
+ */
public static Map<String, GarbageCollectorStatistics> pollStatsSubtractInitial(Map<String, GarbageCollectorStatistics> initial) {
ImmutableMap.Builder<String, GarbageCollectorStatistics> stats = ImmutableMap.builder();
for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/ReportPredicate.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/ReportPredicate.java
new file mode 100644
index 0000000..5d25b09
--- /dev/null
+++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/ReportPredicate.java
@@ -0,0 +1,90 @@
+/*
+ * 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.monitor.tick;
+
+import net.kyori.adventure.text.Component;
+
+/**
+ * A predicate to test whether a tick should be reported.
+ */
+public interface ReportPredicate {
+
+ /**
+ * Tests whether a tick should be reported.
+ *
+ * @param duration the tick duration
+ * @param increaseFromAvg the difference between the ticks duration and the average
+ * @param percentageChange the percentage change between the ticks duration and the average
+ * @return true if the tick should be reported, false otherwise
+ */
+ boolean shouldReport(double duration, double increaseFromAvg, double percentageChange);
+
+ /**
+ * Gets a component to describe how the predicate will select ticks to report.
+ *
+ * @return the component
+ */
+ Component monitoringStartMessage();
+
+ final class PercentageChangeGt implements ReportPredicate {
+ private final double threshold;
+
+ public PercentageChangeGt(double threshold) {
+ this.threshold = threshold;
+ }
+
+ @Override
+ public boolean shouldReport(double duration, double increaseFromAvg, double percentageChange) {
+ if (increaseFromAvg <= 0) {
+ return false;
+ }
+ return percentageChange > this.threshold;
+ }
+
+ @Override
+ public Component monitoringStartMessage() {
+ return Component.text("Starting now, any ticks with >" + this.threshold + "% increase in " +
+ "duration compared to the average will be reported.");
+ }
+ }
+
+ final class DurationGt implements ReportPredicate {
+ private final double threshold;
+
+ public DurationGt(double threshold) {
+ this.threshold = threshold;
+ }
+
+ @Override
+ public boolean shouldReport(double duration, double increaseFromAvg, double percentageChange) {
+ if (increaseFromAvg <= 0) {
+ return false;
+ }
+ return duration > this.threshold;
+ }
+
+ @Override
+ public Component monitoringStartMessage() {
+ return Component.text("Starting now, any ticks with duration >" + this.threshold + " will be reported.");
+ }
+ }
+
+}
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 6b74cef..944fa83 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
@@ -21,29 +21,59 @@
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.tick.TickHook;
+import me.lucko.spark.common.tick.TickHook;
+
import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.format.NamedTextColor;
import java.text.DecimalFormat;
import java.util.DoubleSummaryStatistics;
+import static net.kyori.adventure.text.Component.space;
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+
+/**
+ * Monitoring process for the server/client tick rate.
+ */
public abstract class TickMonitor implements TickHook.Callback, GarbageCollectionMonitor.Listener, AutoCloseable {
- private static final DecimalFormat df = new DecimalFormat("#.##");
+ private static final DecimalFormat DF = new DecimalFormat("#.##");
+ /** The spark platform */
private final SparkPlatform platform;
+ /** The tick hook being used as the source for tick information. */
private final TickHook tickHook;
+ /** The index of the tick when the monitor first started */
private final int zeroTick;
+ /** The active garbage collection monitor, if enabled */
private final GarbageCollectionMonitor garbageCollectionMonitor;
+ /** The predicate used to decide if a tick should be reported. */
private final ReportPredicate reportPredicate;
- // data
+ /**
+ * Enum representing the various phases in a tick monitors lifetime.
+ */
+ private enum Phase {
+ /** Tick monitor is in the setup phase where it determines the average tick rate. */
+ SETUP,
+ /** Tick monitor is in the monitoring phase where it listens for ticks that exceed the threshold. */
+ MONITORING
+ }
+
+ /** The phase the monitor is in */
+ private Phase phase = null;
+ /** Gets the system timestamp of the last recorded tick */
private volatile double lastTickTime = 0;
- private State state = null;
- private final DoubleSummaryStatistics averageTickTime = new DoubleSummaryStatistics();
- private double avg;
+ /** Used to calculate the average tick time during the SETUP phase. */
+ private final DoubleSummaryStatistics averageTickTimeCalc = new DoubleSummaryStatistics();
+ /** The average tick time, defined at the end of the SETUP phase. */
+ private double averageTickTime;
public TickMonitor(SparkPlatform platform, TickHook tickHook, ReportPredicate reportPredicate, boolean monitorGc) {
this.platform = platform;
@@ -65,8 +95,14 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio
protected abstract void sendMessage(Component message);
+ public void start() {
+ this.tickHook.addCallback(this);
+ }
+
@Override
public void close() {
+ this.tickHook.removeCallback(this);
+
if (this.garbageCollectionMonitor != null) {
this.garbageCollectionMonitor.close();
}
@@ -77,10 +113,10 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio
double now = ((double) System.nanoTime()) / 1000000d;
// init
- if (this.state == null) {
- this.state = State.SETUP;
+ if (this.phase == null) {
+ this.phase = Phase.SETUP;
this.lastTickTime = now;
- sendMessage(Component.text("Tick monitor started. Before the monitor becomes fully active, the server's " +
+ sendMessage(text("Tick monitor started. Before the monitor becomes fully active, the server's " +
"average tick rate will be calculated over a period of 120 ticks (approx 6 seconds)."));
return;
}
@@ -95,63 +131,63 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio
}
// form averages
- if (this.state == State.SETUP) {
- this.averageTickTime.accept(tickDuration);
+ if (this.phase == Phase.SETUP) {
+ this.averageTickTimeCalc.accept(tickDuration);
// move onto the next state
- if (this.averageTickTime.getCount() >= 120) {
+ if (this.averageTickTimeCalc.getCount() >= 120) {
this.platform.getPlugin().executeAsync(() -> {
- sendMessage(Component.text("Analysis is now complete.", NamedTextColor.GOLD));
- sendMessage(Component.text()
- .color(NamedTextColor.GRAY)
- .append(Component.text(">", NamedTextColor.WHITE))
- .append(Component.space())
- .append(Component.text("Max: "))
- .append(Component.text(df.format(this.averageTickTime.getMax())))
- .append(Component.text("ms"))
+ sendMessage(text("Analysis is now complete.", GOLD));
+ sendMessage(text()
+ .color(GRAY)
+ .append(text(">", WHITE))
+ .append(space())
+ .append(text("Max: "))
+ .append(text(DF.format(this.averageTickTimeCalc.getMax())))
+ .append(text("ms"))
.build()
);
- sendMessage(Component.text()
- .color(NamedTextColor.GRAY)
- .append(Component.text(">", NamedTextColor.WHITE))
- .append(Component.space())
- .append(Component.text("Min: "))
- .append(Component.text(df.format(this.averageTickTime.getMin())))
- .append(Component.text("ms"))
+ sendMessage(text()
+ .color(GRAY)
+ .append(text(">", WHITE))
+ .append(space())
+ .append(text("Min: "))
+ .append(text(DF.format(this.averageTickTimeCalc.getMin())))
+ .append(text("ms"))
.build()
);
- sendMessage(Component.text()
- .color(NamedTextColor.GRAY)
- .append(Component.text(">", NamedTextColor.WHITE))
- .append(Component.space())
- .append(Component.text("Average: "))
- .append(Component.text(df.format(this.averageTickTime.getAverage())))
- .append(Component.text("ms"))
+ sendMessage(text()
+ .color(GRAY)
+ .append(text(">", WHITE))
+ .append(space())
+ .append(text("Average: "))
+ .append(text(DF.format(this.averageTickTimeCalc.getAverage())))
+ .append(text("ms"))
.build()
);
sendMessage(this.reportPredicate.monitoringStartMessage());
});
- this.avg = this.averageTickTime.getAverage();
- this.state = State.MONITORING;
+ this.averageTickTime = this.averageTickTimeCalc.getAverage();
+ this.phase = Phase.MONITORING;
}
}
- if (this.state == State.MONITORING) {
- double increase = tickDuration - this.avg;
- double percentageChange = (increase * 100d) / this.avg;
+ if (this.phase == Phase.MONITORING) {
+ double increase = tickDuration - this.averageTickTime;
+ double percentageChange = (increase * 100d) / this.averageTickTime;
if (this.reportPredicate.shouldReport(tickDuration, increase, percentageChange)) {
this.platform.getPlugin().executeAsync(() -> {
- sendMessage(Component.text()
- .color(NamedTextColor.GRAY)
- .append(Component.text("Tick "))
- .append(Component.text("#" + getCurrentTick(), NamedTextColor.DARK_GRAY))
- .append(Component.text(" lasted "))
- .append(Component.text(df.format(tickDuration), NamedTextColor.GOLD))
- .append(Component.text(" ms. "))
- .append(Component.text("("))
- .append(Component.text(df.format(percentageChange) + "%", NamedTextColor.GOLD))
- .append(Component.text(" increase from avg)"))
+ sendMessage(text()
+ .color(GRAY)
+ .append(text("Tick "))
+ .append(text("#" + getCurrentTick(), DARK_GRAY))
+ .append(text(" lasted "))
+ .append(text(DF.format(tickDuration), GOLD))
+ .append(text(" ms. "))
+ .append(text("("))
+ .append(text(DF.format(percentageChange) + "%", GOLD))
+ .append(text(" increase from avg)"))
.build()
);
});
@@ -161,105 +197,25 @@ public abstract class TickMonitor implements TickHook.Callback, GarbageCollectio
@Override
public void onGc(GarbageCollectionNotificationInfo data) {
- if (this.state == State.SETUP) {
+ if (this.phase == Phase.SETUP) {
// set lastTickTime to zero so this tick won't be counted in the average
this.lastTickTime = 0;
return;
}
- String gcType;
- if (data.getGcAction().equals("end of minor GC")) {
- gcType = "Young Gen";
- } else if (data.getGcAction().equals("end of major GC")) {
- gcType = "Old Gen";
- } else {
- gcType = data.getGcAction();
- }
-
this.platform.getPlugin().executeAsync(() -> {
- sendMessage(Component.text()
- .color(NamedTextColor.GRAY)
- .append(Component.text("Tick "))
- .append(Component.text("#" + getCurrentTick(), NamedTextColor.DARK_GRAY))
- .append(Component.text(" included "))
- .append(Component.text("GC", NamedTextColor.RED))
- .append(Component.text(" lasting "))
- .append(Component.text(df.format(data.getGcInfo().getDuration()), NamedTextColor.GOLD))
- .append(Component.text(" ms. (type = " + gcType + ")"))
+ sendMessage(text()
+ .color(GRAY)
+ .append(text("Tick "))
+ .append(text("#" + getCurrentTick(), DARK_GRAY))
+ .append(text(" included "))
+ .append(text("GC", RED))
+ .append(text(" lasting "))
+ .append(text(DF.format(data.getGcInfo().getDuration()), GOLD))
+ .append(text(" ms. (type = " + GarbageCollectionMonitor.getGcType(data) + ")"))
.build()
);
});
}
- /**
- * A predicate to test whether a tick should be reported.
- */
- public interface ReportPredicate {
-
- /**
- * Tests whether a tick should be reported.
- *
- * @param duration the tick duration
- * @param increaseFromAvg the difference between the ticks duration and the average
- * @param percentageChange the percentage change between the ticks duration and the average
- * @return true if the tick should be reported, false otherwise
- */
- boolean shouldReport(double duration, double increaseFromAvg, double percentageChange);
-
- /**
- * Gets a component to describe how the predicate will select ticks to report.
- *
- * @return the component
- */
- Component monitoringStartMessage();
-
- final class PercentageChangeGt implements ReportPredicate {
- private final double threshold;
-
- public PercentageChangeGt(double threshold) {
- this.threshold = threshold;
- }
-
- @Override
- public boolean shouldReport(double duration, double increaseFromAvg, double percentageChange) {
- if (increaseFromAvg <= 0) {
- return false;
- }
- return percentageChange > this.threshold;
- }
-
- @Override
- public Component monitoringStartMessage() {
- return Component.text("Starting now, any ticks with >" + this.threshold + "% increase in " +
- "duration compared to the average will be reported.");
- }
- }
-
- final class DurationGt implements ReportPredicate {
- private final double threshold;
-
- public DurationGt(double threshold) {
- this.threshold = threshold;
- }
-
- @Override
- public boolean shouldReport(double duration, double increaseFromAvg, double percentageChange) {
- if (increaseFromAvg <= 0) {
- return false;
- }
- return duration > this.threshold;
- }
-
- @Override
- public Component monitoringStartMessage() {
- return Component.text("Starting now, any ticks with duration >" + this.threshold + " will be reported.");
- }
- }
-
- }
-
- private enum State {
- SETUP,
- MONITORING
- }
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java
index 9e6f7c5..31b58e9 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickStatistics.java
@@ -20,8 +20,8 @@
package me.lucko.spark.common.monitor.tick;
-import me.lucko.spark.common.sampler.tick.TickHook;
-import me.lucko.spark.common.sampler.tick.TickReporter;
+import me.lucko.spark.common.tick.TickHook;
+import me.lucko.spark.common.tick.TickReporter;
import me.lucko.spark.common.util.RollingAverage;
import java.math.BigDecimal;
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 7abe1a7..7dff29e 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
@@ -23,7 +23,7 @@ package me.lucko.spark.common.sampler;
import me.lucko.spark.common.sampler.async.AsyncProfilerAccess;
import me.lucko.spark.common.sampler.async.AsyncSampler;
import me.lucko.spark.common.sampler.java.JavaSampler;
-import me.lucko.spark.common.sampler.tick.TickHook;
+import me.lucko.spark.common.tick.TickHook;
import java.util.concurrent.TimeUnit;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java
index a91a998..8b90639 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java
@@ -23,7 +23,6 @@ package me.lucko.spark.common.sampler.aggregator;
import me.lucko.spark.common.sampler.node.ThreadNode;
import me.lucko.spark.proto.SparkProtos.SamplerMetadata;
-import java.lang.management.ThreadInfo;
import java.util.Map;
/**
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncSampler.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncSampler.java
index a109be7..1e23124 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncSampler.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncSampler.java
@@ -25,11 +25,7 @@ import me.lucko.spark.common.platform.PlatformInfo;
import me.lucko.spark.common.sampler.Sampler;
import me.lucko.spark.common.sampler.ThreadDumper;
import me.lucko.spark.common.sampler.ThreadGrouper;
-import me.lucko.spark.common.sampler.async.jfr.ClassRef;
import me.lucko.spark.common.sampler.async.jfr.JfrReader;
-import me.lucko.spark.common.sampler.async.jfr.MethodRef;
-import me.lucko.spark.common.sampler.async.jfr.Sample;
-import me.lucko.spark.common.sampler.async.jfr.StackTrace;
import me.lucko.spark.common.sampler.node.MergeMode;
import me.lucko.spark.common.sampler.node.ThreadNode;
import me.lucko.spark.proto.SparkProtos;
@@ -206,9 +202,9 @@ public class AsyncSampler implements Sampler {
}
private void readSegments(JfrReader reader, Predicate<String> threadFilter) {
- List<Sample> samples = reader.samples;
+ List<JfrReader.Sample> samples = reader.samples;
for (int i = 0; i < samples.size(); i++) {
- Sample sample = samples.get(i);
+ JfrReader.Sample sample = samples.get(i);
long duration;
if (i == 0) {
@@ -232,8 +228,8 @@ public class AsyncSampler implements Sampler {
}
}
- private static ProfileSegment parseSegment(JfrReader reader, Sample sample, String threadName, long duration) {
- StackTrace stackTrace = reader.stackTraces.get(sample.stackTraceId);
+ private static ProfileSegment parseSegment(JfrReader reader, JfrReader.Sample sample, String threadName, long duration) {
+ JfrReader.StackTrace stackTrace = reader.stackTraces.get(sample.stackTraceId);
int len = stackTrace.methods.length;
AsyncStackTraceElement[] stack = new AsyncStackTraceElement[len];
@@ -250,8 +246,8 @@ public class AsyncSampler implements Sampler {
return result;
}
- MethodRef methodRef = reader.methods.get(methodId);
- ClassRef classRef = reader.classes.get(methodRef.cls);
+ JfrReader.MethodRef methodRef = reader.methods.get(methodId);
+ JfrReader.ClassRef classRef = reader.classes.get(methodRef.cls);
byte[] className = reader.symbols.get(classRef.name);
byte[] methodName = reader.symbols.get(methodRef.name);
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/ClassRef.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/ClassRef.java
deleted file mode 100644
index 2366fa6..0000000
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/ClassRef.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2020 Andrei Pangin
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package me.lucko.spark.common.sampler.async.jfr;
-
-public class ClassRef {
- public final long name;
-
- public ClassRef(long name) {
- this.name = name;
- }
-}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Element.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Element.java
deleted file mode 100644
index 9d6b6c7..0000000
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Element.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2020 Andrei Pangin
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package me.lucko.spark.common.sampler.async.jfr;
-
-class Element {
-
- void addChild(Element e) {
- }
-}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrClass.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrClass.java
deleted file mode 100644
index a171552..0000000
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrClass.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2020 Andrei Pangin
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package me.lucko.spark.common.sampler.async.jfr;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-class JfrClass extends Element {
- final int id;
- final String name;
- final List<JfrField> fields;
-
- JfrClass(Map<String, String> attributes) {
- this.id = Integer.parseInt(attributes.get("id"));
- this.name = attributes.get("name");
- this.fields = new ArrayList<>(2);
- }
-
- @Override
- void addChild(Element e) {
- if (e instanceof JfrField) {
- fields.add((JfrField) e);
- }
- }
-
- JfrField field(String name) {
- for (JfrField field : fields) {
- if (field.name.equals(name)) {
- return field;
- }
- }
- return null;
- }
-}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrField.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrField.java
deleted file mode 100644
index 7a78f2c..0000000
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrField.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2020 Andrei Pangin
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package me.lucko.spark.common.sampler.async.jfr;
-
-import java.util.Map;
-
-class JfrField extends Element {
- final String name;
- final int type;
- final boolean constantPool;
-
- JfrField(Map<String, String> attributes) {
- this.name = attributes.get("name");
- this.type = Integer.parseInt(attributes.get("class"));
- this.constantPool = "true".equals(attributes.get("constantPool"));
- }
-}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrReader.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrReader.java
index 49a3474..95c9bad 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrReader.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrReader.java
@@ -24,7 +24,6 @@ import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collections;
@@ -386,4 +385,100 @@ public class JfrReader implements Closeable {
buf.get(bytes);
return bytes;
}
+
+ public static class ClassRef {
+ public final long name;
+
+ public ClassRef(long name) {
+ this.name = name;
+ }
+ }
+
+ static class Element {
+
+ void addChild(Element e) {
+ }
+ }
+
+ static class JfrClass extends Element {
+ final int id;
+ final String name;
+ final List<JfrField> fields;
+
+ JfrClass(Map<String, String> attributes) {
+ this.id = Integer.parseInt(attributes.get("id"));
+ this.name = attributes.get("name");
+ this.fields = new ArrayList<>(2);
+ }
+
+ @Override
+ void addChild(Element e) {
+ if (e instanceof JfrField) {
+ fields.add((JfrField) e);
+ }
+ }
+
+ JfrField field(String name) {
+ for (JfrField field : fields) {
+ if (field.name.equals(name)) {
+ return field;
+ }
+ }
+ return null;
+ }
+ }
+
+ static class JfrField extends Element {
+ final String name;
+ final int type;
+ final boolean constantPool;
+
+ JfrField(Map<String, String> attributes) {
+ this.name = attributes.get("name");
+ this.type = Integer.parseInt(attributes.get("class"));
+ this.constantPool = "true".equals(attributes.get("constantPool"));
+ }
+ }
+
+ public static class MethodRef {
+ public final long cls;
+ public final long name;
+ public final long sig;
+
+ public MethodRef(long cls, long name, long sig) {
+ this.cls = cls;
+ this.name = name;
+ this.sig = sig;
+ }
+ }
+
+ public static class Sample implements Comparable<Sample> {
+ public final long time;
+ public final int tid;
+ public final int stackTraceId;
+ public final int threadState;
+
+ public Sample(long time, int tid, int stackTraceId, int threadState) {
+ this.time = time;
+ this.tid = tid;
+ this.stackTraceId = stackTraceId;
+ this.threadState = threadState;
+ }
+
+ @Override
+ public int compareTo(Sample o) {
+ return Long.compare(time, o.time);
+ }
+ }
+
+ public static class StackTrace {
+ public final long[] methods;
+ public final byte[] types;
+ public long samples;
+
+ public StackTrace(long[] methods, byte[] types) {
+ this.methods = methods;
+ this.types = types;
+ }
+ }
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/MethodRef.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/MethodRef.java
deleted file mode 100644
index 2f9071e..0000000
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/MethodRef.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2020 Andrei Pangin
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package me.lucko.spark.common.sampler.async.jfr;
-
-public class MethodRef {
- public final long cls;
- public final long name;
- public final long sig;
-
- public MethodRef(long cls, long name, long sig) {
- this.cls = cls;
- this.name = name;
- this.sig = sig;
- }
-}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Sample.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Sample.java
deleted file mode 100644
index 095e261..0000000
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Sample.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2020 Andrei Pangin
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package me.lucko.spark.common.sampler.async.jfr;
-
-public class Sample implements Comparable<Sample> {
- public final long time;
- public final int tid;
- public final int stackTraceId;
- public final int threadState;
-
- public Sample(long time, int tid, int stackTraceId, int threadState) {
- this.time = time;
- this.tid = tid;
- this.stackTraceId = stackTraceId;
- this.threadState = threadState;
- }
-
- @Override
- public int compareTo(Sample o) {
- return Long.compare(time, o.time);
- }
-}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/StackTrace.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/StackTrace.java
deleted file mode 100644
index 4c12c5e..0000000
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/StackTrace.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2020 Andrei Pangin
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package me.lucko.spark.common.sampler.async.jfr;
-
-public class StackTrace {
- public final long[] methods;
- public final byte[] types;
- public long samples;
-
- public StackTrace(long[] methods, byte[] types) {
- this.methods = methods;
- this.types = types;
- }
-}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/java/JavaSampler.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/java/JavaSampler.java
index 568609e..0ee5f86 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/java/JavaSampler.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/java/JavaSampler.java
@@ -22,6 +22,7 @@
package me.lucko.spark.common.sampler.java;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.platform.PlatformInfo;
import me.lucko.spark.common.sampler.Sampler;
@@ -29,7 +30,7 @@ import me.lucko.spark.common.sampler.ThreadDumper;
import me.lucko.spark.common.sampler.ThreadGrouper;
import me.lucko.spark.common.sampler.node.MergeMode;
import me.lucko.spark.common.sampler.node.ThreadNode;
-import me.lucko.spark.common.sampler.tick.TickHook;
+import me.lucko.spark.common.tick.TickHook;
import me.lucko.spark.proto.SparkProtos.SamplerData;
import me.lucko.spark.proto.SparkProtos.SamplerMetadata;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/java/TickedDataAggregator.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/java/TickedDataAggregator.java
index 1a0bcdd..018a3b8 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/java/TickedDataAggregator.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/java/TickedDataAggregator.java
@@ -23,7 +23,7 @@ package me.lucko.spark.common.sampler.java;
import me.lucko.spark.common.sampler.ThreadGrouper;
import me.lucko.spark.common.sampler.aggregator.DataAggregator;
import me.lucko.spark.common.sampler.node.ThreadNode;
-import me.lucko.spark.common.sampler.tick.TickHook;
+import me.lucko.spark.common.tick.TickHook;
import me.lucko.spark.proto.SparkProtos.SamplerMetadata;
import java.lang.management.ThreadInfo;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/node/StackTraceNode.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/node/StackTraceNode.java
index bd731c1..4179464 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/node/StackTraceNode.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/node/StackTraceNode.java
@@ -24,7 +24,6 @@ package me.lucko.spark.common.sampler.node;
import me.lucko.spark.common.util.MethodDisambiguator;
import me.lucko.spark.proto.SparkProtos;
-import java.util.Comparator;
import java.util.Objects;
/**
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickHook.java b/spark-common/src/main/java/me/lucko/spark/common/tick/AbstractTickHook.java
index 72e927f..a6e8745 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickHook.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/tick/AbstractTickHook.java
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package me.lucko.spark.common.sampler.tick;
+package me.lucko.spark.common.tick;
import java.util.HashSet;
import java.util.Set;
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/tick/AbstractTickReporter.java
index 4005f87..74a814d 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/tick/AbstractTickReporter.java
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package me.lucko.spark.common.sampler.tick;
+package me.lucko.spark.common.tick;
import java.util.HashSet;
import java.util.Set;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickHook.java b/spark-common/src/main/java/me/lucko/spark/common/tick/TickHook.java
index 8731216..e673258 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickHook.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/tick/TickHook.java
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package me.lucko.spark.common.sampler.tick;
+package me.lucko.spark.common.tick;
/**
* A hook with the game's "tick loop".
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/tick/TickReporter.java
index e922e72..b575f59 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/tick/TickReporter.java
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package me.lucko.spark.common.sampler.tick;
+package me.lucko.spark.common.tick;
/**
* A reporting callback for the game's "tick loop".
diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/AbstractHttpClient.java b/spark-common/src/main/java/me/lucko/spark/common/util/AbstractHttpClient.java
index 1ff169d..8ece3d4 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/util/AbstractHttpClient.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/util/AbstractHttpClient.java
@@ -38,6 +38,7 @@ public class AbstractHttpClient {
protected Response makeHttpRequest(Request request) throws IOException {
Response response = this.okHttp.newCall(request).execute();
if (!response.isSuccessful()) {
+ response.close();
throw new RuntimeException("Request was unsuccessful: " + response.code() + " - " + response.message());
}
return response;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java b/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java
index ff8f4e3..9202303 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/util/BytebinClient.java
@@ -51,11 +51,7 @@ public class BytebinClient extends AbstractHttpClient {
*/
public BytebinClient(OkHttpClient okHttpClient, String url, String userAgent) {
super(okHttpClient);
- if (url.endsWith("/")) {
- this.url = url;
- } else {
- this.url = url + "/";
- }
+ this.url = url + (url.endsWith("/") ? "" : "/");
this.userAgent = userAgent;
}
@@ -64,11 +60,10 @@ public class BytebinClient extends AbstractHttpClient {
*
* @param buf the compressed content
* @param contentType the type of the content
- * @param allowModification if the paste should be modifiable
* @return the key of the resultant content
* @throws IOException if an error occurs
*/
- public Content postContent(byte[] buf, MediaType contentType, boolean allowModification) throws IOException {
+ public Content postContent(byte[] buf, MediaType contentType) throws IOException {
RequestBody body = RequestBody.create(contentType, buf);
Request.Builder requestBuilder = new Request.Builder()
@@ -76,69 +71,21 @@ public class BytebinClient extends AbstractHttpClient {
.header("User-Agent", this.userAgent)
.header("Content-Encoding", "gzip");
- if (allowModification) {
- requestBuilder.header("Allow-Modification", "true");
- }
-
Request request = requestBuilder.post(body).build();
try (Response response = makeHttpRequest(request)) {
String key = response.header("Location");
if (key == null) {
throw new IllegalStateException("Key not returned");
}
-
- if (allowModification) {
- String modificationKey = response.header("Modification-Key");
- if (modificationKey == null) {
- throw new IllegalStateException("Modification key not returned");
- }
- return new Content(key, modificationKey);
- } else {
- return new Content(key);
- }
- }
- }
-
- /**
- * PUTs modified GZIP compressed content to bytebin in place of existing content.
- *
- * @param existingContent the existing content
- * @param buf the compressed content to put
- * @param contentType the type of the content
- * @throws IOException if an error occurs
- */
- public void modifyContent(Content existingContent, byte[] buf, MediaType contentType) throws IOException {
- if (!existingContent.modifiable) {
- throw new IllegalArgumentException("Existing content is not modifiable");
+ return new Content(key);
}
-
- RequestBody body = RequestBody.create(contentType, buf);
-
- Request.Builder requestBuilder = new Request.Builder()
- .url(this.url + existingContent.key())
- .header("User-Agent", this.userAgent)
- .header("Content-Encoding", "gzip")
- .header("Modification-Key", existingContent.modificationKey);
-
- Request request = requestBuilder.put(body).build();
- makeHttpRequest(request).close();
}
public static final class Content {
private final String key;
- private final boolean modifiable;
- private final String modificationKey;
Content(String key) {
this.key = key;
- this.modifiable = false;
- this.modificationKey = null;
- }
-
- Content(String key, String modificationKey) {
- this.key = key;
- this.modifiable = true;
- this.modificationKey = modificationKey;
}
public String key() {
diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/MethodDisambiguator.java b/spark-common/src/main/java/me/lucko/spark/common/util/MethodDisambiguator.java
index e6311e0..2e113a9 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/util/MethodDisambiguator.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/util/MethodDisambiguator.java
@@ -23,7 +23,9 @@ package me.lucko.spark.common.util;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
+
import me.lucko.spark.common.sampler.node.StackTraceNode;
+
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
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 8160e96..2c6219a 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
@@ -23,9 +23,7 @@ package me.lucko.spark.common.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayDeque;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.Queue;
public class RollingAverage {
diff --git a/spark-common/src/main/java/me/lucko/spark/common/util/ThreadFinder.java b/spark-common/src/main/java/me/lucko/spark/common/util/ThreadFinder.java
index 0d1cbd3..66aa941 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/util/ThreadFinder.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/util/ThreadFinder.java
@@ -21,7 +21,6 @@
package me.lucko.spark.common.util;
import java.util.Arrays;
-import java.util.Objects;
import java.util.stream.Stream;
/**
@@ -56,7 +55,7 @@ public final class ThreadFinder {
threads = new Thread[threads.length * 2];
}
this.approxActiveCount = len;
- return Arrays.stream(threads).filter(Objects::nonNull);
+ return Arrays.stream(threads, 0, len);
}
}