Skip to content

Commit

Permalink
Add module for Paper
Browse files Browse the repository at this point in the history
  • Loading branch information
lucko committed Jul 17, 2024
1 parent 1b75abc commit 8b7a929
Show file tree
Hide file tree
Showing 27 changed files with 1,085 additions and 40 deletions.
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ include (
'spark-api',
'spark-common',
'spark-bukkit',
'spark-paper',
'spark-bungeecord',
'spark-velocity',
'spark-velocity4',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import me.lucko.spark.common.util.Configuration;
import me.lucko.spark.common.util.SparkStaticLogger;
import me.lucko.spark.common.util.TemporaryFiles;
import me.lucko.spark.common.util.classfinder.ClassFinder;
import me.lucko.spark.common.ws.TrustedKeyStore;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
Expand Down Expand Up @@ -285,6 +286,10 @@ public ClassSourceLookup createClassSourceLookup() {
return this.plugin.createClassSourceLookup();
}

public ClassFinder createClassFinder() {
return this.plugin.createClassFinder();
}

public TickStatistics getTickStatistics() {
return this.tickStatistics;
}
Expand Down
15 changes: 15 additions & 0 deletions spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
import me.lucko.spark.common.sampler.source.SourceMetadata;
import me.lucko.spark.common.tick.TickHook;
import me.lucko.spark.common.tick.TickReporter;
import me.lucko.spark.common.util.classfinder.ClassFinder;
import me.lucko.spark.common.util.classfinder.FallbackClassFinder;
import me.lucko.spark.common.util.classfinder.InstrumentationClassFinder;

import java.nio.file.Path;
import java.util.Collection;
Expand Down Expand Up @@ -149,6 +152,18 @@ default ClassSourceLookup createClassSourceLookup() {
return ClassSourceLookup.NO_OP;
}

/**
* Creates a class finder for the platform.
*
* @return the class finder
*/
default ClassFinder createClassFinder() {
return ClassFinder.combining(
new InstrumentationClassFinder(this),
FallbackClassFinder.INSTANCE
);
}

/**
* Gets a list of known sources (plugins/mods) on the platform.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ private Sampler.ExportProps getExportProps(SparkPlatform platform, CommandRespon
.creator(resp.senderData())
.comment(Iterables.getFirst(arguments.stringFlag("comment"), null))
.mergeMode(() -> {
MethodDisambiguator methodDisambiguator = new MethodDisambiguator();
MethodDisambiguator methodDisambiguator = new MethodDisambiguator(platform.createClassFinder());
return arguments.boolFlag("separate-parent-calls")
? MergeMode.separateParentCalls(methodDisambiguator)
: MergeMode.sameMethod(methodDisambiguator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import me.lucko.spark.common.sampler.source.SourceMetadata;
import me.lucko.spark.common.sampler.window.ProtoTimeEncoder;
import me.lucko.spark.common.sampler.window.WindowStatisticsCollector;
import me.lucko.spark.common.util.classfinder.ClassFinder;
import me.lucko.spark.common.ws.ViewerSocket;
import me.lucko.spark.proto.SparkProtos;
import me.lucko.spark.proto.SparkSamplerProtos.SamplerData;
Expand All @@ -44,6 +45,7 @@
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;

/**
* Base implementation class for {@link Sampler}s.
Expand Down Expand Up @@ -230,11 +232,11 @@ protected void writeMetadataToProto(SamplerData.Builder proto, SparkPlatform pla
proto.setMetadata(metadata);
}

protected void writeDataToProto(SamplerData.Builder proto, DataAggregator dataAggregator, MergeMode mergeMode, ClassSourceLookup classSourceLookup) {
protected void writeDataToProto(SamplerData.Builder proto, DataAggregator dataAggregator, MergeMode mergeMode, ClassSourceLookup classSourceLookup, Supplier<ClassFinder> classFinderSupplier) {
List<ThreadNode> data = dataAggregator.exportData();
data.sort(Comparator.comparing(ThreadNode::getThreadLabel));

ClassSourceLookup.Visitor classSourceVisitor = ClassSourceLookup.createVisitor(classSourceLookup);
ClassSourceLookup.Visitor classSourceVisitor = ClassSourceLookup.createVisitor(classSourceLookup, classFinderSupplier);

ProtoTimeEncoder timeEncoder = new ProtoTimeEncoder(getMode().valueTransformer(), data);
int[] timeWindows = timeEncoder.getKeys();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public SamplerData toProto(SparkPlatform platform, ExportProps exportProps) {
proto.setChannelInfo(exportProps.channelInfo());
}
writeMetadataToProto(proto, platform, exportProps.creator(), exportProps.comment(), this.dataAggregator);
writeDataToProto(proto, this.dataAggregator, exportProps.mergeMode().get(), exportProps.classSourceLookup().get());
writeDataToProto(proto, this.dataAggregator, exportProps.mergeMode().get(), exportProps.classSourceLookup().get(), platform::createClassFinder);
return proto.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public SamplerData toProto(SparkPlatform platform, ExportProps exportProps) {
proto.setChannelInfo(exportProps.channelInfo());
}
writeMetadataToProto(proto, platform, exportProps.creator(), exportProps.comment(), this.dataAggregator);
writeDataToProto(proto, this.dataAggregator, exportProps.mergeMode().get(), exportProps.classSourceLookup().get());
writeDataToProto(proto, this.dataAggregator, exportProps.mergeMode().get(), exportProps.classSourceLookup().get(), platform::createClassFinder);
return proto.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.sampler.node.StackTraceNode;
import me.lucko.spark.common.sampler.node.ThreadNode;
import me.lucko.spark.common.util.ClassFinder;
import me.lucko.spark.common.util.classfinder.ClassFinder;
import me.lucko.spark.common.util.classfinder.InstrumentationClassFinder;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.io.IOException;
Expand All @@ -42,6 +43,7 @@
import java.util.Objects;
import java.util.Queue;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -203,11 +205,11 @@ interface Visitor {
Map<String, String> getLineSourceMapping();
}

static Visitor createVisitor(ClassSourceLookup lookup) {
static Visitor createVisitor(ClassSourceLookup lookup, Supplier<ClassFinder> classFinderSupplier) {
if (lookup == ClassSourceLookup.NO_OP) {
return NoOpVisitor.INSTANCE; // don't bother!
}
return new VisitorImpl(lookup);
return new VisitorImpl(lookup, classFinderSupplier.get());
}

enum NoOpVisitor implements Visitor {
Expand Down Expand Up @@ -254,14 +256,15 @@ public Map<String, String> getLineSourceMapping() {
*/
class VisitorImpl implements Visitor {
private final ClassSourceLookup lookup;
private final ClassFinder classFinder = new ClassFinder();
private final ClassFinder classFinder;

private final SourcesMap<String> classSources = new SourcesMap<>(Function.identity());
private final SourcesMap<MethodCall> methodSources = new SourcesMap<>(MethodCall::toString);
private final SourcesMap<MethodCallByLine> lineSources = new SourcesMap<>(MethodCallByLine::toString);

VisitorImpl(ClassSourceLookup lookup) {
VisitorImpl(ClassSourceLookup lookup, ClassFinder classFinder) {
this.lookup = lookup;
this.classFinder = classFinder;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import me.lucko.spark.common.sampler.node.StackTraceNode;
import me.lucko.spark.common.util.classfinder.ClassFinder;
import me.lucko.spark.common.util.classfinder.InstrumentationClassFinder;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
Expand All @@ -43,8 +45,13 @@
* to a method (method name + method description).
*/
public final class MethodDisambiguator {
private final Map<String, ComputedClass> cache = new ConcurrentHashMap<>();
private final ClassFinder classFinder = new ClassFinder();
private final ClassFinder classFinder;
private final Map<String, ComputedClass> cache;

public MethodDisambiguator(ClassFinder classFinder) {
this.classFinder = classFinder;
this.cache = new ConcurrentHashMap<>();
}

public Optional<MethodDescription> disambiguate(StackTraceNode element) {
String desc = element.getMethodDescription();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* This file is part of spark.
*
* Copyright (c) lucko (Luck) <[email protected]>
* Copyright (c) contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package me.lucko.spark.common.util.classfinder;

import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;

public interface ClassFinder {

/**
* Creates a ClassFinder that combines the results of multiple other finders.
*
* @param finders the other class finders
* @return the combined class finder
*/
static ClassFinder combining(ClassFinder... finders) {
return new CombinedClassFinder(ImmutableList.copyOf(finders));
}

/**
* Attempts to find a class by name.
*
* @param className the name of the class
* @return the class, if found
*/
@Nullable Class<?> findClass(String className);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* This file is part of spark.
*
* Copyright (c) lucko (Luck) <[email protected]>
* Copyright (c) contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package me.lucko.spark.common.util.classfinder;

import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.List;

class CombinedClassFinder implements ClassFinder {
private final List<ClassFinder> finders;

CombinedClassFinder(List<ClassFinder> finders) {
this.finders = finders;
}

@Override
public @Nullable Class<?> findClass(String className) {
for (ClassFinder finder : this.finders) {
Class<?> clazz = finder.findClass(className);
if (clazz != null) {
return clazz;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* This file is part of spark.
*
* Copyright (c) lucko (Luck) <[email protected]>
* Copyright (c) contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package me.lucko.spark.common.util.classfinder;

import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Uses {@link Class#forName(String)} to find a class reference for given class names.
*/
public enum FallbackClassFinder implements ClassFinder {
INSTANCE;

@Override
public @Nullable Class<?> findClass(String className) {
try {
return Class.forName(className);
} catch (Throwable e) {
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package me.lucko.spark.common.util;
package me.lucko.spark.common.util.classfinder;

import me.lucko.spark.common.SparkPlugin;
import me.lucko.spark.common.util.JavaVersion;
import me.lucko.spark.common.util.SparkStaticLogger;
import net.bytebuddy.agent.ByteBuddyAgent;
import org.checkerframework.checker.nullness.qual.Nullable;

Expand All @@ -33,18 +36,18 @@
*
* <p>This is necessary as we don't always have access to the classloader for a given class.</p>
*/
public class ClassFinder {
public class InstrumentationClassFinder implements ClassFinder {

private static boolean warned = false;

private static Instrumentation loadInstrumentation() {
private static Instrumentation loadInstrumentation(SparkPlugin plugin) {
Instrumentation instrumentation = null;
try {
instrumentation = ByteBuddyAgent.install();
if (!warned && JavaVersion.getJavaVersion() >= 21) {
warned = true;
SparkStaticLogger.log(Level.INFO, "If you see a warning above that says \"WARNING: A Java agent has been loaded dynamically\", it can be safely ignored.");
SparkStaticLogger.log(Level.INFO, "See here for more information: https://spark.lucko.me/docs/misc/Java-agent-warning");
plugin.log(Level.INFO, "If you see a warning above that says \"WARNING: A Java agent has been loaded dynamically\", it can be safely ignored.");
plugin.log(Level.INFO, "See here for more information: https://spark.lucko.me/docs/misc/Java-agent-warning");
}
} catch (Exception e) {
// ignored
Expand All @@ -54,8 +57,8 @@ private static Instrumentation loadInstrumentation() {

private final Map<String, Class<?>> classes = new HashMap<>();

public ClassFinder() {
Instrumentation instrumentation = loadInstrumentation();
public InstrumentationClassFinder(SparkPlugin plugin) {
Instrumentation instrumentation = loadInstrumentation(plugin);
if (instrumentation == null) {
return;
}
Expand All @@ -66,21 +69,9 @@ public ClassFinder() {
}
}

@Override
public @Nullable Class<?> findClass(String className) {
// try instrumentation
Class<?> clazz = this.classes.get(className);
if (clazz != null) {
return clazz;
}

// try Class.forName
try {
return Class.forName(className);
} catch (Throwable e) {
// ignore
}

return null;
return this.classes.get(className);
}

}
Loading

0 comments on commit 8b7a929

Please sign in to comment.