From 7079484d428321c9b3db09394577efda4d591a4e Mon Sep 17 00:00:00 2001 From: Luck Date: Mon, 19 Sep 2022 18:57:02 +0100 Subject: Provide extra metadata about sources in sampler data --- .../main/java/me/lucko/spark/common/command/modules/SamplerModule.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 0a80c31..2afed64 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 @@ -38,6 +38,7 @@ 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.source.ClassSourceLookup; import me.lucko.spark.common.tick.TickHook; import me.lucko.spark.common.util.MethodDisambiguator; import me.lucko.spark.proto.SparkSamplerProtos; @@ -303,7 +304,7 @@ public class SamplerModule implements CommandModule { } private void handleUpload(SparkPlatform platform, CommandResponseHandler resp, Sampler sampler, ThreadNodeOrder threadOrder, String comment, MergeMode mergeMode, boolean saveToFileFlag) { - SparkSamplerProtos.SamplerData output = sampler.toProto(platform, resp.sender(), threadOrder, comment, mergeMode, platform.createClassSourceLookup()); + SparkSamplerProtos.SamplerData output = sampler.toProto(platform, resp.sender(), threadOrder, comment, mergeMode, ClassSourceLookup.create(platform)); boolean saveToFile = false; if (saveToFileFlag) { -- cgit From d31f3c7bdf03c874ff9518d47d060adc18322d6b Mon Sep 17 00:00:00 2001 From: lucko Date: Fri, 7 Oct 2022 20:26:24 +0100 Subject: Split profiler output into windows (#253) --- .../lucko/spark/common/command/modules/SamplerModule.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 2afed64..c1e4981 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 @@ -35,7 +35,6 @@ import me.lucko.spark.common.sampler.Sampler; import me.lucko.spark.common.sampler.SamplerBuilder; import me.lucko.spark.common.sampler.ThreadDumper; import me.lucko.spark.common.sampler.ThreadGrouper; -import me.lucko.spark.common.sampler.ThreadNodeOrder; import me.lucko.spark.common.sampler.async.AsyncSampler; import me.lucko.spark.common.sampler.node.MergeMode; import me.lucko.spark.common.sampler.source.ClassSourceLookup; @@ -94,7 +93,6 @@ public class SamplerModule implements CommandModule { .argumentUsage("not-combined", null) .argumentUsage("force-java-sampler", null) .argumentUsage("stop --comment", "comment") - .argumentUsage("stop --order-by-time", null) .argumentUsage("stop --save-to-file", null) .executor(this::profiler) .tabCompleter((platform, sender, arguments) -> { @@ -103,7 +101,7 @@ public class SamplerModule implements CommandModule { } if (arguments.contains("--stop") || arguments.contains("--upload")) { - return TabCompleter.completeForOpts(arguments, "--order-by-time", "--comment", "--save-to-file"); + return TabCompleter.completeForOpts(arguments, "--comment", "--save-to-file"); } List opts = new ArrayList<>(Arrays.asList("--info", "--stop", "--cancel", @@ -249,14 +247,13 @@ public class SamplerModule implements CommandModule { // 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); boolean saveToFile = arguments.boolFlag("save-to-file"); future.thenAcceptAsync(s -> { resp.broadcastPrefixed(text("The active profiler has completed! Uploading results...")); - handleUpload(platform, resp, s, threadOrder, comment, mergeMode, saveToFile); + handleUpload(platform, resp, s, comment, mergeMode, saveToFile); }); } } @@ -293,18 +290,17 @@ public class SamplerModule implements CommandModule { } 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); boolean saveToFile = arguments.boolFlag("save-to-file"); - handleUpload(platform, resp, this.activeSampler, threadOrder, comment, mergeMode, saveToFile); + handleUpload(platform, resp, this.activeSampler, comment, mergeMode, saveToFile); this.activeSampler = null; } } - private void handleUpload(SparkPlatform platform, CommandResponseHandler resp, Sampler sampler, ThreadNodeOrder threadOrder, String comment, MergeMode mergeMode, boolean saveToFileFlag) { - SparkSamplerProtos.SamplerData output = sampler.toProto(platform, resp.sender(), threadOrder, comment, mergeMode, ClassSourceLookup.create(platform)); + private void handleUpload(SparkPlatform platform, CommandResponseHandler resp, Sampler sampler, String comment, MergeMode mergeMode, boolean saveToFileFlag) { + SparkSamplerProtos.SamplerData output = sampler.toProto(platform, resp.sender(), comment, mergeMode, ClassSourceLookup.create(platform)); boolean saveToFile = false; if (saveToFileFlag) { -- cgit From fafc14712fa78001b431241bd961ca429d6f74bc Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 27 Oct 2022 23:35:27 +0100 Subject: Tidy up command feedback messages --- .../common/command/modules/SamplerModule.java | 91 +++++++++++++--------- 1 file changed, 56 insertions(+), 35 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 c1e4981..6dbf913 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 @@ -42,6 +42,7 @@ import me.lucko.spark.common.tick.TickHook; import me.lucko.spark.common.util.MethodDisambiguator; import me.lucko.spark.proto.SparkSamplerProtos; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import java.io.IOException; @@ -62,6 +63,7 @@ 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; public class SamplerModule implements CommandModule { private static final String SPARK_SAMPLER_MEDIA_TYPE = "application/x-spark-sampler"; @@ -83,17 +85,11 @@ public class SamplerModule implements CommandModule { .aliases("profiler", "sampler") .argumentUsage("info", null) .argumentUsage("stop", null) - .argumentUsage("cancel", null) - .argumentUsage("interval", "interval millis") + .argumentUsage("timeout", "timeout seconds") + .argumentUsage("thread *", null) .argumentUsage("thread", "thread name") .argumentUsage("only-ticks-over", "tick length millis") - .argumentUsage("timeout", "timeout seconds") - .argumentUsage("regex --thread", "thread regex") - .argumentUsage("combine-all", null) - .argumentUsage("not-combined", null) - .argumentUsage("force-java-sampler", null) - .argumentUsage("stop --comment", "comment") - .argumentUsage("stop --save-to-file", null) + .argumentUsage("interval", "interval millis") .executor(this::profiler) .tabCompleter((platform, sender, arguments) -> { if (arguments.contains("--info") || arguments.contains("--cancel")) { @@ -120,7 +116,7 @@ public class SamplerModule implements CommandModule { private void profiler(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) { if (arguments.boolFlag("info")) { - profilerInfo(resp); + profilerInfo(platform, resp); return; } @@ -138,6 +134,11 @@ public class SamplerModule implements CommandModule { } private void profilerStart(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) { + if (this.activeSampler != null) { + profilerInfo(platform, resp); + 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. " + @@ -194,12 +195,7 @@ public class SamplerModule implements CommandModule { } } - if (this.activeSampler != null) { - resp.replyPrefixed(text("An active profiler is already running.")); - return; - } - - resp.broadcastPrefixed(text("Initializing a new profiler, please wait...")); + resp.broadcastPrefixed(text("Starting a new profiler, please wait...")); SamplerBuilder builder = new SamplerBuilder(); builder.threadDumper(threadDumper); @@ -217,13 +213,16 @@ public class SamplerModule implements CommandModule { Sampler sampler = this.activeSampler = builder.start(platform); resp.broadcastPrefixed(text() - .append(text("Profiler now active!", GOLD)) + .append(text("Profiler is now running!", 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.")); + resp.broadcastPrefixed(text("It will run in the background until it is stopped by an admin.")); + resp.broadcastPrefixed(text("To stop the profiler and upload the results, run:")); + resp.broadcastPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler --stop")); } else { resp.broadcastPrefixed(text("The results will be automatically returned after the profiler has been running for " + timeoutSeconds + " seconds.")); } @@ -258,20 +257,28 @@ public class SamplerModule implements CommandModule { } } - private void profilerInfo(CommandResponseHandler resp) { + private void profilerInfo(SparkPlatform platform, CommandResponseHandler resp) { if (this.activeSampler == null) { - resp.replyPrefixed(text("There isn't an active profiler running.")); + resp.replyPrefixed(text("The profiler isn't running!")); + resp.replyPrefixed(text("To start a new one, run:")); + resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler")); } else { + resp.replyPrefixed(text("Profiler is already running!", GOLD)); + + long runningTime = (System.currentTimeMillis() - this.activeSampler.getStartTime()) / 1000L; + resp.replyPrefixed(text("So far, it has profiled for " + runningTime + " seconds.")); + long timeout = this.activeSampler.getAutoEndTime(); if (timeout == -1) { - resp.replyPrefixed(text("There is an active profiler currently running, with no defined timeout.")); + resp.replyPrefixed(text("To stop the profiler and upload the results, run:")); + resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler --stop")); } else { long timeoutDiff = (timeout - System.currentTimeMillis()) / 1000L; - resp.replyPrefixed(text("There is an active profiler currently running, due to timeout in " + timeoutDiff + " seconds.")); + resp.replyPrefixed(text("It is due to complete automatically and upload results in " + timeoutDiff + " seconds.")); } - long runningTime = (System.currentTimeMillis() - this.activeSampler.getStartTime()) / 1000L; - resp.replyPrefixed(text("It has been profiling for " + runningTime + " seconds so far.")); + resp.replyPrefixed(text("To cancel the profiler without uploading the results, run:")); + resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler --cancel")); } } @@ -280,7 +287,7 @@ public class SamplerModule implements CommandModule { resp.replyPrefixed(text("There isn't an active profiler running.")); } else { close(); - resp.broadcastPrefixed(text("The active profiler has been cancelled.", GOLD)); + resp.broadcastPrefixed(text("Profiler has been cancelled.", GOLD)); } } @@ -289,11 +296,17 @@ public class SamplerModule implements CommandModule { 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...")); + + boolean saveToFile = arguments.boolFlag("save-to-file"); + if (saveToFile) { + resp.broadcastPrefixed(text("Stopping the profiler & saving results, please wait...")); + } else { + resp.broadcastPrefixed(text("Stopping the profiler & uploading results, please wait...")); + } + 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); - boolean saveToFile = arguments.boolFlag("save-to-file"); handleUpload(platform, resp, this.activeSampler, comment, mergeMode, saveToFile); this.activeSampler = null; } @@ -310,7 +323,7 @@ public class SamplerModule implements CommandModule { String key = platform.getBytebinClient().postContent(output, SPARK_SAMPLER_MEDIA_TYPE).key(); String url = platform.getViewerUrl() + key; - resp.broadcastPrefixed(text("Profiler results:", GOLD)); + resp.broadcastPrefixed(text("Profiler stopped & upload complete!", GOLD)); resp.broadcast(text() .content(url) .color(GRAY) @@ -331,13 +344,9 @@ public class SamplerModule implements CommandModule { try { Files.write(file, output.toByteArray()); - resp.broadcastPrefixed(text() - .content("Profile written to: ") - .color(GOLD) - .append(text(file.toString(), GRAY)) - .build() - ); - resp.broadcastPrefixed(text("You can read the profile file using the viewer web-app - " + platform.getViewerUrl(), GRAY)); + resp.broadcastPrefixed(text("Profiler stopped & save complete!", GOLD)); + resp.broadcastPrefixed(text("Data has been written to: " + file)); + resp.broadcastPrefixed(text("You can view the profile file using the web app @ " + platform.getViewerUrl(), GRAY)); platform.getActivityLog().addToLog(Activity.fileActivity(resp.sender(), System.currentTimeMillis(), "Profiler", file.toString())); } catch (IOException e) { @@ -346,4 +355,16 @@ public class SamplerModule implements CommandModule { } } } + + private static Component cmdPrompt(String cmd) { + return text() + .append(text(" ")) + .append(text() + .content(cmd) + .color(WHITE) + .clickEvent(ClickEvent.runCommand(cmd)) + .build() + ) + .build(); + } } -- cgit From 76f43ab59d3839600bd9e040ff2d09199ebe778a Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 13 Nov 2022 19:15:54 +0000 Subject: Limit profile length to 1 hour --- .../common/command/modules/SamplerModule.java | 50 +++++++++------------- 1 file changed, 20 insertions(+), 30 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 6dbf913..00bf1a9 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 @@ -68,17 +68,6 @@ import static net.kyori.adventure.text.format.NamedTextColor.WHITE; public class SamplerModule implements CommandModule { private static final String SPARK_SAMPLER_MEDIA_TYPE = "application/x-spark-sampler"; - /** The sampler instance currently running, if any */ - private Sampler activeSampler = null; - - @Override - public void close() { - if (this.activeSampler != null) { - this.activeSampler.stop(); - this.activeSampler = null; - } - } - @Override public void registerCommands(Consumer consumer) { consumer.accept(Command.builder() @@ -121,7 +110,7 @@ public class SamplerModule implements CommandModule { } if (arguments.boolFlag("cancel")) { - profilerCancel(resp); + profilerCancel(platform, resp); return; } @@ -134,7 +123,7 @@ public class SamplerModule implements CommandModule { } private void profilerStart(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) { - if (this.activeSampler != null) { + if (platform.getSamplerContainer().getActiveSampler() != null) { profilerInfo(platform, resp); return; } @@ -210,7 +199,8 @@ public class SamplerModule implements CommandModule { if (ticksOver != -1) { builder.ticksOver(ticksOver, tickHook); } - Sampler sampler = this.activeSampler = builder.start(platform); + Sampler sampler = builder.start(platform); + platform.getSamplerContainer().setActiveSampler(sampler); resp.broadcastPrefixed(text() .append(text("Profiler is now running!", GOLD)) @@ -227,7 +217,7 @@ public class SamplerModule implements CommandModule { resp.broadcastPrefixed(text("The results will be automatically returned after the profiler has been running for " + timeoutSeconds + " seconds.")); } - CompletableFuture future = this.activeSampler.getFuture(); + CompletableFuture future = sampler.getFuture(); // send message if profiling fails future.whenCompleteAsync((s, throwable) -> { @@ -238,11 +228,7 @@ public class SamplerModule implements CommandModule { }); // set activeSampler to null when complete. - future.whenCompleteAsync((s, throwable) -> { - if (sampler == this.activeSampler) { - this.activeSampler = null; - } - }); + sampler.getFuture().whenCompleteAsync((s, throwable) -> platform.getSamplerContainer().unsetActiveSampler(s)); // await the result if (timeoutSeconds != -1) { @@ -258,17 +244,18 @@ public class SamplerModule implements CommandModule { } private void profilerInfo(SparkPlatform platform, CommandResponseHandler resp) { - if (this.activeSampler == null) { + Sampler sampler = platform.getSamplerContainer().getActiveSampler(); + if (sampler == null) { resp.replyPrefixed(text("The profiler isn't running!")); resp.replyPrefixed(text("To start a new one, run:")); resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler")); } else { resp.replyPrefixed(text("Profiler is already running!", GOLD)); - long runningTime = (System.currentTimeMillis() - this.activeSampler.getStartTime()) / 1000L; + long runningTime = (System.currentTimeMillis() - sampler.getStartTime()) / 1000L; resp.replyPrefixed(text("So far, it has profiled for " + runningTime + " seconds.")); - long timeout = this.activeSampler.getAutoEndTime(); + long timeout = sampler.getAutoEndTime(); if (timeout == -1) { resp.replyPrefixed(text("To stop the profiler and upload the results, run:")); resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler --stop")); @@ -282,20 +269,24 @@ public class SamplerModule implements CommandModule { } } - private void profilerCancel(CommandResponseHandler resp) { - if (this.activeSampler == null) { + private void profilerCancel(SparkPlatform platform, CommandResponseHandler resp) { + Sampler sampler = platform.getSamplerContainer().getActiveSampler(); + if (sampler == null) { resp.replyPrefixed(text("There isn't an active profiler running.")); } else { - close(); + platform.getSamplerContainer().stopActiveSampler(); resp.broadcastPrefixed(text("Profiler has been cancelled.", GOLD)); } } private void profilerStop(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) { - if (this.activeSampler == null) { + Sampler sampler = platform.getSamplerContainer().getActiveSampler(); + + if (sampler == null) { resp.replyPrefixed(text("There isn't an active profiler running.")); } else { - this.activeSampler.stop(); + platform.getSamplerContainer().unsetActiveSampler(sampler); + sampler.stop(); boolean saveToFile = arguments.boolFlag("save-to-file"); if (saveToFile) { @@ -307,8 +298,7 @@ public class SamplerModule implements CommandModule { 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, comment, mergeMode, saveToFile); - this.activeSampler = null; + handleUpload(platform, resp, sampler, comment, mergeMode, saveToFile); } } -- cgit From f2d77d875f32f107987c93da1f90529fc6812444 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 13 Nov 2022 21:24:57 +0000 Subject: Background profiler --- .../me/lucko/spark/common/command/Arguments.java | 11 +- .../me/lucko/spark/common/command/Command.java | 58 ++++++++++- .../common/command/modules/GcMonitoringModule.java | 22 +--- .../common/command/modules/SamplerModule.java | 115 +++++++++++++++------ 4 files changed, 145 insertions(+), 61 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 17c49e2..ad8c777 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 @@ -38,8 +38,9 @@ public class Arguments { private final List rawArgs; private final SetMultimap parsedArgs; + private String parsedSubCommand = null; - public Arguments(List rawArgs) { + public Arguments(List rawArgs, boolean allowSubCommand) { this.rawArgs = rawArgs; this.parsedArgs = HashMultimap.create(); @@ -52,7 +53,9 @@ public class Arguments { Matcher matcher = FLAG_REGEX.matcher(arg); boolean matches = matcher.matches(); - if (flag == null || matches) { + if (i == 0 && allowSubCommand && !matches) { + this.parsedSubCommand = arg; + } else if (flag == null || matches) { if (!matches) { throw new ParseException("Expected flag at position " + i + " but got '" + arg + "' instead!"); } @@ -80,6 +83,10 @@ public class Arguments { return this.rawArgs; } + public String subCommand() { + return this.parsedSubCommand; + } + public int intFlag(String key) { Iterator it = this.parsedArgs.get(key).iterator(); if (it.hasNext()) { 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 dad15e6..c6871a9 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 @@ -25,10 +25,17 @@ import com.google.common.collect.ImmutableList; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.command.sender.CommandSender; +import net.kyori.adventure.text.Component; + import java.util.Collections; import java.util.List; import java.util.Objects; +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.GRAY; + public class Command { public static Builder builder() { @@ -39,12 +46,14 @@ public class Command { private final List arguments; private final Executor executor; private final TabCompleter tabCompleter; + private final boolean allowSubCommand; - private Command(List aliases, List arguments, Executor executor, TabCompleter tabCompleter) { + private Command(List aliases, List arguments, Executor executor, TabCompleter tabCompleter, boolean allowSubCommand) { this.aliases = aliases; this.arguments = arguments; this.executor = executor; this.tabCompleter = tabCompleter; + this.allowSubCommand = allowSubCommand; } public List aliases() { @@ -67,11 +76,16 @@ public class Command { return this.aliases.get(0); } + public boolean allowSubCommand() { + return this.allowSubCommand; + } + public static final class Builder { private final ImmutableList.Builder aliases = ImmutableList.builder(); private final ImmutableList.Builder arguments = ImmutableList.builder(); private Executor executor = null; private TabCompleter tabCompleter = null; + private boolean allowSubCommand = false; Builder() { @@ -82,8 +96,13 @@ public class Command { return this; } + public Builder argumentUsage(String subCommandName, String argumentName, String parameterDescription) { + this.arguments.add(new ArgumentInfo(subCommandName, argumentName, parameterDescription)); + return this; + } + public Builder argumentUsage(String argumentName, String parameterDescription) { - this.arguments.add(new ArgumentInfo(argumentName, parameterDescription)); + this.arguments.add(new ArgumentInfo("", argumentName, parameterDescription)); return this; } @@ -97,6 +116,11 @@ public class Command { return this; } + public Builder allowSubCommand(boolean allowSubCommand) { + this.allowSubCommand = allowSubCommand; + return this; + } + public Command build() { List aliases = this.aliases.build(); if (aliases.isEmpty()) { @@ -108,7 +132,7 @@ public class Command { if (this.tabCompleter == null) { this.tabCompleter = TabCompleter.empty(); } - return new Command(aliases, this.arguments.build(), this.executor, this.tabCompleter); + return new Command(aliases, this.arguments.build(), this.executor, this.tabCompleter, this.allowSubCommand); } } @@ -127,14 +151,20 @@ public class Command { } public static final class ArgumentInfo { + private final String subCommandName; private final String argumentName; private final String parameterDescription; - public ArgumentInfo(String argumentName, String parameterDescription) { + public ArgumentInfo(String subCommandName, String argumentName, String parameterDescription) { + this.subCommandName = subCommandName; this.argumentName = argumentName; this.parameterDescription = parameterDescription; } + public String subCommandName() { + return this.subCommandName; + } + public String argumentName() { return this.argumentName; } @@ -146,6 +176,26 @@ public class Command { public boolean requiresParameter() { return this.parameterDescription != null; } + + public Component toComponent(String padding) { + if (requiresParameter()) { + return text() + .content(padding) + .append(text("[", DARK_GRAY)) + .append(text("--" + argumentName(), GRAY)) + .append(space()) + .append(text("<" + parameterDescription() + ">", DARK_GRAY)) + .append(text("]", DARK_GRAY)) + .build(); + } else { + return text() + .content(padding) + .append(text("[", DARK_GRAY)) + .append(text("--" + argumentName(), GRAY)) + .append(text("]", DARK_GRAY)) + .build(); + } + } } } 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 2ce83fd..a2da0a0 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 @@ -123,7 +123,7 @@ public class GcMonitoringModule implements CommandModule { ); report.add(text() .content(" ") - .append(text(formatTime((long) averageFrequency), WHITE)) + .append(text(FormatUtil.formatSeconds((long) averageFrequency / 1000), WHITE)) .append(text(" avg frequency", GRAY)) .build() ); @@ -153,26 +153,6 @@ public class GcMonitoringModule implements CommandModule { ); } - private static String formatTime(long millis) { - if (millis <= 0) { - return "0s"; - } - - long second = millis / 1000; - long minute = second / 60; - second = second % 60; - - StringBuilder sb = new StringBuilder(); - if (minute != 0) { - sb.append(minute).append("m "); - } - if (second != 0) { - sb.append(second).append("s "); - } - - return sb.toString().trim(); - } - private static class ReportingGcMonitor extends GarbageCollectionMonitor implements GarbageCollectionMonitor.Listener { private final SparkPlatform platform; private final CommandResponseHandler resp; 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 00bf1a9..6a76748 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 @@ -39,6 +39,7 @@ import me.lucko.spark.common.sampler.async.AsyncSampler; import me.lucko.spark.common.sampler.node.MergeMode; import me.lucko.spark.common.sampler.source.ClassSourceLookup; import me.lucko.spark.common.tick.TickHook; +import me.lucko.spark.common.util.FormatUtil; import me.lucko.spark.common.util.MethodDisambiguator; import me.lucko.spark.proto.SparkSamplerProtos; @@ -72,31 +73,36 @@ public class SamplerModule implements CommandModule { public void registerCommands(Consumer consumer) { consumer.accept(Command.builder() .aliases("profiler", "sampler") - .argumentUsage("info", null) - .argumentUsage("stop", null) - .argumentUsage("timeout", "timeout seconds") - .argumentUsage("thread *", null) - .argumentUsage("thread", "thread name") - .argumentUsage("only-ticks-over", "tick length millis") - .argumentUsage("interval", "interval millis") + .allowSubCommand(true) + .argumentUsage("info", "", null) + .argumentUsage("start", "timeout", "timeout seconds") + .argumentUsage("start", "thread *", null) + .argumentUsage("start", "thread", "thread name") + .argumentUsage("start", "only-ticks-over", "tick length millis") + .argumentUsage("start", "interval", "interval millis") + .argumentUsage("stop", "", null) + .argumentUsage("cancel", "", null) .executor(this::profiler) .tabCompleter((platform, sender, arguments) -> { - if (arguments.contains("--info") || arguments.contains("--cancel")) { - return Collections.emptyList(); + List opts = Collections.emptyList(); + + if (arguments.size() > 0) { + String subCommand = arguments.get(0); + if (subCommand.equals("stop") || subCommand.equals("upload")) { + opts = new ArrayList<>(Arrays.asList("--comment", "--save-to-file")); + opts.removeAll(arguments); + } + if (subCommand.equals("start")) { + opts = new ArrayList<>(Arrays.asList("--timeout", "--regex", "--combine-all", + "--not-combined", "--interval", "--only-ticks-over", "--force-java-sampler")); + opts.removeAll(arguments); + opts.add("--thread"); // allowed multiple times + } } - if (arguments.contains("--stop") || arguments.contains("--upload")) { - return TabCompleter.completeForOpts(arguments, "--comment", "--save-to-file"); - } - - List opts = new ArrayList<>(Arrays.asList("--info", "--stop", "--cancel", - "--timeout", "--regex", "--combine-all", "--not-combined", "--interval", - "--only-ticks-over", "--force-java-sampler")); - opts.removeAll(arguments); - opts.add("--thread"); // allowed multiple times - return TabCompleter.create() - .from(0, CompletionSupplier.startsWith(opts)) + .at(0, CompletionSupplier.startsWith(Arrays.asList("info", "start", "stop", "cancel"))) + .from(1, CompletionSupplier.startsWith(opts)) .complete(arguments); }) .build() @@ -104,28 +110,48 @@ public class SamplerModule implements CommandModule { } private void profiler(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) { - if (arguments.boolFlag("info")) { + String subCommand = arguments.subCommand() == null ? "" : arguments.subCommand(); + + if (subCommand.equals("info") || arguments.boolFlag("info")) { profilerInfo(platform, resp); return; } - if (arguments.boolFlag("cancel")) { + if (subCommand.equals("cancel") || arguments.boolFlag("cancel")) { profilerCancel(platform, resp); return; } - if (arguments.boolFlag("stop") || arguments.boolFlag("upload")) { + if (subCommand.equals("stop") || arguments.boolFlag("stop") || arguments.boolFlag("upload")) { profilerStop(platform, sender, resp, arguments); return; } - profilerStart(platform, sender, resp, arguments); + if (subCommand.equals("start") || arguments.boolFlag("start")) { + profilerStart(platform, sender, resp, arguments); + return; + } + + if (arguments.raw().isEmpty()) { + profilerInfo(platform, resp); + } else { + profilerStart(platform, sender, resp, arguments); + } } private void profilerStart(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments) { - if (platform.getSamplerContainer().getActiveSampler() != null) { - profilerInfo(platform, resp); - return; + Sampler previousSampler = platform.getSamplerContainer().getActiveSampler(); + if (previousSampler != null) { + if (previousSampler.isRunningInBackground()) { + // there is a background profiler running - stop that first + resp.replyPrefixed(text("Stopping the background profiler before starting... please wait")); + previousSampler.stop(); + platform.getSamplerContainer().unsetActiveSampler(previousSampler); + } else { + // there is a non-background profiler running - tell the user + profilerInfo(platform, resp); + return; + } } int timeoutSeconds = arguments.intFlag("timeout"); @@ -212,9 +238,9 @@ public class SamplerModule implements CommandModule { if (timeoutSeconds == -1) { resp.broadcastPrefixed(text("It will run in the background until it is stopped by an admin.")); resp.broadcastPrefixed(text("To stop the profiler and upload the results, run:")); - resp.broadcastPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler --stop")); + resp.broadcastPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler stop")); } else { - resp.broadcastPrefixed(text("The results will be automatically returned after the profiler has been running for " + timeoutSeconds + " seconds.")); + resp.broadcastPrefixed(text("The results will be automatically returned after the profiler has been running for " + FormatUtil.formatSeconds(timeoutSeconds) + ".")); } CompletableFuture future = sampler.getFuture(); @@ -248,24 +274,34 @@ public class SamplerModule implements CommandModule { if (sampler == null) { resp.replyPrefixed(text("The profiler isn't running!")); resp.replyPrefixed(text("To start a new one, run:")); - resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler")); + resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler start")); } else { resp.replyPrefixed(text("Profiler is already running!", GOLD)); long runningTime = (System.currentTimeMillis() - sampler.getStartTime()) / 1000L; - resp.replyPrefixed(text("So far, it has profiled for " + runningTime + " seconds.")); + + if (sampler.isRunningInBackground()) { + resp.replyPrefixed(text() + .append(text("It was started ")) + .append(text("automatically", WHITE)) + .append(text(" when spark enabled and has been running in the background for " + FormatUtil.formatSeconds(runningTime) + ".")) + .build() + ); + } else { + resp.replyPrefixed(text("So far, it has profiled for " + FormatUtil.formatSeconds(runningTime) + ".")); + } long timeout = sampler.getAutoEndTime(); if (timeout == -1) { resp.replyPrefixed(text("To stop the profiler and upload the results, run:")); - resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler --stop")); + resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler stop")); } else { long timeoutDiff = (timeout - System.currentTimeMillis()) / 1000L; - resp.replyPrefixed(text("It is due to complete automatically and upload results in " + timeoutDiff + " seconds.")); + resp.replyPrefixed(text("It is due to complete automatically and upload results in " + FormatUtil.formatSeconds(timeoutDiff) + ".")); } resp.replyPrefixed(text("To cancel the profiler without uploading the results, run:")); - resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler --cancel")); + resp.replyPrefixed(cmdPrompt("/" + platform.getPlugin().getCommandName() + " profiler cancel")); } } @@ -299,6 +335,17 @@ public class SamplerModule implements CommandModule { MethodDisambiguator methodDisambiguator = new MethodDisambiguator(); MergeMode mergeMode = arguments.boolFlag("separate-parent-calls") ? MergeMode.separateParentCalls(methodDisambiguator) : MergeMode.sameMethod(methodDisambiguator); handleUpload(platform, resp, sampler, comment, mergeMode, saveToFile); + + // if the previous sampler was running in the background, create a new one + if (platform.getSamplerContainer().isBackgroundProfilerEnabled()) { + platform.startBackgroundProfiler(); + + resp.broadcastPrefixed(text() + .append(text("Restarted the background profiler. ")) + .append(text("(If you don't want this to happen, run: /" + platform.getPlugin().getCommandName() + " profiler cancel)", DARK_GRAY)) + .build() + ); + } } } -- cgit From b9f0e49ed17a7c32f36f31141c02529359944d03 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 13 Nov 2022 22:42:49 +0000 Subject: Add upload subcommand as per documentation oops --- .../main/java/me/lucko/spark/common/command/modules/SamplerModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 6a76748..00cd4fa 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 @@ -122,7 +122,7 @@ public class SamplerModule implements CommandModule { return; } - if (subCommand.equals("stop") || arguments.boolFlag("stop") || arguments.boolFlag("upload")) { + if (subCommand.equals("stop") || subCommand.equals("upload") || arguments.boolFlag("stop") || arguments.boolFlag("upload")) { profilerStop(platform, sender, resp, arguments); return; } -- cgit From 65f9460a1a27e930b3749525766fd44d57b65300 Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 26 Nov 2022 23:00:53 +0000 Subject: Include player/entity/chunk counts in window statistics --- .../java/me/lucko/spark/common/command/modules/SamplerModule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 00cd4fa..f576eac 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 @@ -145,7 +145,7 @@ public class SamplerModule implements CommandModule { if (previousSampler.isRunningInBackground()) { // there is a background profiler running - stop that first resp.replyPrefixed(text("Stopping the background profiler before starting... please wait")); - previousSampler.stop(); + previousSampler.stop(true); platform.getSamplerContainer().unsetActiveSampler(previousSampler); } else { // there is a non-background profiler running - tell the user @@ -310,7 +310,7 @@ public class SamplerModule implements CommandModule { if (sampler == null) { resp.replyPrefixed(text("There isn't an active profiler running.")); } else { - platform.getSamplerContainer().stopActiveSampler(); + platform.getSamplerContainer().stopActiveSampler(true); resp.broadcastPrefixed(text("Profiler has been cancelled.", GOLD)); } } @@ -322,7 +322,7 @@ public class SamplerModule implements CommandModule { resp.replyPrefixed(text("There isn't an active profiler running.")); } else { platform.getSamplerContainer().unsetActiveSampler(sampler); - sampler.stop(); + sampler.stop(false); boolean saveToFile = arguments.boolFlag("save-to-file"); if (saveToFile) { -- cgit From fc1e371d67551e9548491e9bf50534d91ce5d170 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 27 Nov 2022 23:38:21 +0000 Subject: Temporary solution to async-profiler JVM crashing issues (#271, #273, #274) --- .../java/me/lucko/spark/common/command/modules/SamplerModule.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'spark-common/src/main/java/me/lucko/spark/common/command') 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 f576eac..cd00f0d 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 @@ -337,9 +337,7 @@ public class SamplerModule implements CommandModule { handleUpload(platform, resp, sampler, comment, mergeMode, saveToFile); // if the previous sampler was running in the background, create a new one - if (platform.getSamplerContainer().isBackgroundProfilerEnabled()) { - platform.startBackgroundProfiler(); - + if (platform.getBackgroundSamplerManager().restartBackgroundSampler()) { resp.broadcastPrefixed(text() .append(text("Restarted the background profiler. ")) .append(text("(If you don't want this to happen, run: /" + platform.getPlugin().getCommandName() + " profiler cancel)", DARK_GRAY)) -- cgit