204 lines
7.4 KiB
Java
204 lines
7.4 KiB
Java
package org.embeddedt.modernfix.spark;
|
|
|
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
|
|
import me.lucko.spark.common.SparkPlatform;
|
|
import me.lucko.spark.common.SparkPlugin;
|
|
import me.lucko.spark.common.command.sender.CommandSender;
|
|
import me.lucko.spark.common.platform.PlatformInfo;
|
|
import me.lucko.spark.common.sampler.Sampler;
|
|
import me.lucko.spark.common.sampler.SamplerSettings;
|
|
import me.lucko.spark.common.sampler.ThreadDumper;
|
|
import me.lucko.spark.common.sampler.ThreadGrouper;
|
|
import me.lucko.spark.common.sampler.async.AsyncSampler;
|
|
import me.lucko.spark.common.sampler.async.SampleCollector;
|
|
import me.lucko.spark.common.sampler.java.JavaSampler;
|
|
import me.lucko.spark.common.sampler.java.MergeStrategy;
|
|
import me.lucko.spark.lib.adventure.text.Component;
|
|
import me.lucko.spark.proto.SparkSamplerProtos;
|
|
import net.minecraft.SharedConstants;
|
|
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
|
|
import java.nio.file.Path;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.logging.Level;
|
|
import java.util.stream.Stream;
|
|
|
|
/* Inspired by CensoredASM */
|
|
public class SparkLaunchProfiler {
|
|
private static PlatformInfo platformInfo = new ModernFixPlatformInfo();
|
|
private static CommandSender commandSender = new ModernFixCommandSender();
|
|
private static Map<String, Sampler> ongoingSamplers = new Object2ReferenceOpenHashMap<>();
|
|
private static ExecutorService executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setDaemon(true).setNameFormat("spark-modernfix-async-worker").build());
|
|
private static final SparkPlatform platform = new SparkPlatform(new ModernFixSparkPlugin());
|
|
|
|
private static final String ALLOW_SPARK_PROFILING_PROP = "modernfix.allowSparkProfiling";
|
|
private static final boolean USE_JAVA_SAMPLER_FOR_LAUNCH = !Boolean.getBoolean("modernfix.profileWithAsyncSampler");
|
|
private static final boolean ALLOW_SPARK_PROFILING = Boolean.getBoolean(ALLOW_SPARK_PROFILING_PROP);
|
|
private static final int SAMPLING_INTERVAL = Integer.getInteger("modernfix.profileSamplingIntervalMicroseconds", 4000);
|
|
private static final String THREAD_GROUPER = System.getProperty("modernfix.profileSamplingThreadGrouper", "by-pool");
|
|
|
|
private static boolean checkSparkProfilingAllowed() {
|
|
if (!ALLOW_SPARK_PROFILING) {
|
|
ModernFixMixinPlugin.instance.logger.fatal("To reduce excessive load on the Spark servers, you must set " +
|
|
"-D{}=true in your JVM arguments for profiling to proceed. Please do " +
|
|
"this and relaunch the game.", ALLOW_SPARK_PROFILING_PROP);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static void start(String key) {
|
|
if (!checkSparkProfilingAllowed()) {
|
|
return;
|
|
}
|
|
if (!ongoingSamplers.containsKey(key)) {
|
|
Sampler sampler;
|
|
SamplerSettings settings = new SamplerSettings(SAMPLING_INTERVAL, ThreadDumper.ALL, ThreadGrouper.parseConfigSetting(THREAD_GROUPER).get(), -1, false, true);
|
|
try {
|
|
if(USE_JAVA_SAMPLER_FOR_LAUNCH) {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
sampler = new AsyncSampler(platform, settings, new SampleCollector.Execution(SAMPLING_INTERVAL));
|
|
} catch (UnsupportedOperationException e) {
|
|
sampler = new JavaSampler(platform, settings);
|
|
}
|
|
ongoingSamplers.put(key, sampler);
|
|
ModernFixMixinPlugin.instance.logger.warn("Profiler has started for stage [{}]...", key);
|
|
sampler.start();
|
|
}
|
|
}
|
|
|
|
public static void stop(String key) {
|
|
Sampler sampler = ongoingSamplers.remove(key);
|
|
if (sampler != null) {
|
|
sampler.stop(true);
|
|
output(key, sampler);
|
|
}
|
|
}
|
|
|
|
private static void output(String key, Sampler sampler) {
|
|
executor.execute(() -> {
|
|
ModernFixMixinPlugin.instance.logger.warn("Stage [{}] profiler has stopped! Uploading results...", key);
|
|
SparkSamplerProtos.SamplerData output = sampler.toProto(platform, new Sampler.ExportProps()
|
|
.creator(new CommandSender.Data(commandSender.getName(), commandSender.getUniqueId()))
|
|
.comment("Stage: " + key)
|
|
.mergeStrategy(MergeStrategy.SAME_METHOD)
|
|
.classSourceLookup(platform::createClassSourceLookup));
|
|
try {
|
|
String urlKey = platform.getBytebinClient().postContent(output, "application/x-spark-sampler").key();
|
|
String url = "https://spark.lucko.me/" + urlKey;
|
|
ModernFixMixinPlugin.instance.logger.warn("Profiler results for Stage [{}]: {}", key, url);
|
|
} catch (Exception e) {
|
|
ModernFixMixinPlugin.instance.logger.fatal("An error occurred whilst uploading the results.", e);
|
|
}
|
|
});
|
|
}
|
|
|
|
static class ModernFixPlatformInfo implements PlatformInfo {
|
|
|
|
@Override
|
|
public Type getType() {
|
|
return ModernFixPlatformHooks.INSTANCE.isClient() ? Type.CLIENT : Type.SERVER;
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return ModernFixPlatformHooks.INSTANCE.getPlatformName();
|
|
}
|
|
|
|
@Override
|
|
public String getBrand() {
|
|
return this.getName();
|
|
}
|
|
|
|
@Override
|
|
public String getVersion() {
|
|
return ModernFixPlatformHooks.INSTANCE.getVersionString();
|
|
}
|
|
|
|
@Override
|
|
public String getMinecraftVersion() {
|
|
return SharedConstants.getCurrentVersion().name();
|
|
}
|
|
|
|
}
|
|
|
|
public static class ModernFixCommandSender implements CommandSender {
|
|
|
|
private final UUID uuid = UUID.randomUUID();
|
|
private final String name;
|
|
|
|
public ModernFixCommandSender() {
|
|
this.name = "ModernFix";
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
@Override
|
|
public UUID getUniqueId() {
|
|
return uuid;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasPermission(String s) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void sendMessage(Component component) {
|
|
|
|
}
|
|
}
|
|
|
|
static class ModernFixSparkPlugin implements SparkPlugin {
|
|
|
|
@Override
|
|
public String getVersion() {
|
|
return "1.0";
|
|
}
|
|
|
|
@Override
|
|
public Path getPluginDirectory() {
|
|
return ModernFixPlatformHooks.INSTANCE.getGameDirectory().resolve("spark-modernfix");
|
|
}
|
|
|
|
@Override
|
|
public String getCommandName() {
|
|
return "spark-modernfix";
|
|
}
|
|
|
|
@Override
|
|
public Stream<? extends CommandSender> getCommandSenders() {
|
|
return Stream.of();
|
|
}
|
|
|
|
@Override
|
|
public void executeAsync(Runnable runnable) {
|
|
executor.execute(runnable);
|
|
}
|
|
|
|
@Override
|
|
public void log(Level level, String s) {
|
|
ModernFixMixinPlugin.instance.logger.warn(s);
|
|
}
|
|
|
|
@Override
|
|
public void log(Level level, String s, Throwable t) {
|
|
ModernFixMixinPlugin.instance.logger.warn(s, t);
|
|
}
|
|
|
|
@Override
|
|
public PlatformInfo getPlatformInfo() {
|
|
return platformInfo;
|
|
}
|
|
}
|
|
}
|