From 0fd7d30f2a9027c2bd9df3215759c6d91d110acc Mon Sep 17 00:00:00 2001 From: Luck Date: Tue, 23 Mar 2021 00:52:44 +0000 Subject: Refactor and tidy up, more consistent code style --- .../java/me/lucko/spark/common/SparkPlatform.java | 19 +- .../java/me/lucko/spark/common/SparkPlugin.java | 4 +- .../lucko/spark/common/activitylog/Activity.java | 111 +++++ .../spark/common/activitylog/ActivityLog.java | 86 ---- .../me/lucko/spark/common/command/Arguments.java | 14 +- .../me/lucko/spark/common/command/Command.java | 1 + .../common/command/CommandResponseHandler.java | 10 +- .../common/command/modules/ActivityLogModule.java | 16 +- .../common/command/modules/GcMonitoringModule.java | 36 +- .../spark/common/command/modules/HealthModule.java | 510 +++++++++++---------- .../common/command/modules/HeapAnalysisModule.java | 292 ++++++------ .../common/command/modules/SamplerModule.java | 365 ++++++++------- .../command/modules/TickMonitoringModule.java | 21 +- .../spark/common/command/sender/CommandSender.java | 2 + .../me/lucko/spark/common/heapdump/HeapDump.java | 24 +- .../spark/common/heapdump/HeapDumpSummary.java | 1 + .../monitor/memory/GarbageCollectionMonitor.java | 43 +- .../monitor/memory/GarbageCollectorStatistics.java | 14 + .../spark/common/monitor/tick/ReportPredicate.java | 90 ++++ .../spark/common/monitor/tick/TickMonitor.java | 236 ++++------ .../spark/common/monitor/tick/TickStatistics.java | 4 +- .../lucko/spark/common/sampler/SamplerBuilder.java | 2 +- .../common/sampler/aggregator/DataAggregator.java | 1 - .../spark/common/sampler/async/AsyncSampler.java | 16 +- .../spark/common/sampler/async/jfr/ClassRef.java | 25 - .../spark/common/sampler/async/jfr/Element.java | 23 - .../spark/common/sampler/async/jfr/JfrClass.java | 49 -- .../spark/common/sampler/async/jfr/JfrField.java | 31 -- .../spark/common/sampler/async/jfr/JfrReader.java | 97 +++- .../spark/common/sampler/async/jfr/MethodRef.java | 29 -- .../spark/common/sampler/async/jfr/Sample.java | 36 -- .../spark/common/sampler/async/jfr/StackTrace.java | 28 -- .../spark/common/sampler/java/JavaSampler.java | 3 +- .../common/sampler/java/TickedDataAggregator.java | 2 +- .../spark/common/sampler/node/StackTraceNode.java | 1 - .../common/sampler/tick/AbstractTickHook.java | 53 --- .../common/sampler/tick/AbstractTickReporter.java | 45 -- .../lucko/spark/common/sampler/tick/TickHook.java | 64 --- .../spark/common/sampler/tick/TickReporter.java | 57 --- .../lucko/spark/common/tick/AbstractTickHook.java | 53 +++ .../spark/common/tick/AbstractTickReporter.java | 45 ++ .../java/me/lucko/spark/common/tick/TickHook.java | 64 +++ .../me/lucko/spark/common/tick/TickReporter.java | 57 +++ .../spark/common/util/AbstractHttpClient.java | 1 + .../me/lucko/spark/common/util/BytebinClient.java | 59 +-- .../spark/common/util/MethodDisambiguator.java | 2 + .../me/lucko/spark/common/util/RollingAverage.java | 2 - .../me/lucko/spark/common/util/ThreadFinder.java | 3 +- 48 files changed, 1386 insertions(+), 1361 deletions(-) create mode 100644 spark-common/src/main/java/me/lucko/spark/common/activitylog/Activity.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/monitor/tick/ReportPredicate.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/ClassRef.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Element.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrClass.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrField.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/MethodRef.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Sample.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/StackTrace.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickHook.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/AbstractTickReporter.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickHook.java delete mode 100644 spark-common/src/main/java/me/lucko/spark/common/sampler/tick/TickReporter.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/tick/AbstractTickHook.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/tick/AbstractTickReporter.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/tick/TickHook.java create mode 100644 spark-common/src/main/java/me/lucko/spark/common/tick/TickReporter.java (limited to 'spark-common/src') 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) + * 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 . + */ + +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 { 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 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 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 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 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 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 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 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 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 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 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 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 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(" / ")) -