From 44516b1d8a04b9b93b50d4fdba7298ce84411a10 Mon Sep 17 00:00:00 2001 From: Jason <11360596+jpenilla@users.noreply.github.com> Date: Mon, 19 Jul 2021 19:22:18 -0700 Subject: [PATCH] [ci skip] Put mappings util in a separate class to the stacktrace deobfuscator (#6230) --- ...ktraces-in-log-messages-crash-report.patch | 186 ++++++++++-------- ...nd-timings-for-sensors-and-behaviors.patch | 48 ++--- 2 files changed, 129 insertions(+), 105 deletions(-) diff --git a/patches/server/0715-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0715-Deobfuscate-stacktraces-in-log-messages-crash-report.patch index 48062a0c79..9cbb9e92e8 100644 --- a/patches/server/0715-Deobfuscate-stacktraces-in-log-messages-crash-report.patch +++ b/patches/server/0715-Deobfuscate-stacktraces-in-log-messages-crash-report.patch @@ -157,60 +157,49 @@ index 0000000000000000000000000000000000000000..d019802a36dbaca4bf299a55d28381e4 + return new StacktraceDeobfuscatingRewritePolicy(); + } +} -diff --git a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java +diff --git a/src/main/java/io/papermc/paper/util/ObfHelper.java b/src/main/java/io/papermc/paper/util/ObfHelper.java new file mode 100644 -index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d026a7061f +index 0000000000000000000000000000000000000000..271e0a2ec549836a32565bb9100d432cd68d6046 --- /dev/null -+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java -@@ -0,0 +1,227 @@ ++++ b/src/main/java/io/papermc/paper/util/ObfHelper.java +@@ -0,0 +1,87 @@ +package io.papermc.paper.util; + -+import com.destroystokyo.paper.PaperConfig; +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; -+import it.unimi.dsi.fastutil.ints.IntArrayList; -+import it.unimi.dsi.fastutil.ints.IntList; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; -+import java.util.Collections; -+import java.util.HashMap; -+import java.util.LinkedHashMap; +import java.util.Map; +import net.fabricmc.mapping.tree.ClassDef; +import net.fabricmc.mapping.tree.MethodDef; +import net.fabricmc.mapping.tree.TinyMappingFactory; +import net.fabricmc.mapping.tree.TinyTree; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+import org.objectweb.asm.ClassReader; -+import org.objectweb.asm.ClassVisitor; -+import org.objectweb.asm.Label; -+import org.objectweb.asm.MethodVisitor; -+import org.objectweb.asm.Opcodes; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; + -+public enum StacktraceDeobfuscator { ++@DefaultQualifier(NonNull.class) ++public enum ObfHelper { + INSTANCE; + -+ private static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn"; -+ private static final String SPIGOT_NAMESPACE = "spigot"; ++ public static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn"; ++ public static final String SPIGOT_NAMESPACE = "spigot"; + + private final @Nullable Map mappings; -+ private final Map, Map, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(64, 0.75f, true) { -+ @Override -+ protected boolean removeEldestEntry(final Map.Entry, Map, IntList>> eldest) { -+ return this.size() > 63; -+ } -+ }); + -+ StacktraceDeobfuscator() { ++ ObfHelper() { + this.mappings = loadMappingsIfPresent(); + } + ++ public @Nullable Map mappings() { ++ return this.mappings; ++ } ++ + private static @Nullable Map loadMappingsIfPresent() { -+ try (final InputStream mappingsInputStream = StacktraceDeobfuscator.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) { ++ try (final @Nullable InputStream mappingsInputStream = StacktraceDeobfuscator.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) { + if (mappingsInputStream == null) { + return null; + } @@ -249,6 +238,55 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0 + } + } + ++ public record ClassMapping( ++ String obfName, ++ String mojangName, ++ Map, MethodMapping> methodMappings ++ ) {} ++ ++ public record MethodMapping( ++ String obfName, ++ String mojangName, ++ String descriptor ++ ) {} ++} +diff --git a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6ef365dfd3caa0a726983fc57125f4d9a86959fc +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java +@@ -0,0 +1,156 @@ ++package io.papermc.paper.util; ++ ++import com.destroystokyo.paper.PaperConfig; ++import com.mojang.datafixers.util.Pair; ++import it.unimi.dsi.fastutil.ints.IntArrayList; ++import it.unimi.dsi.fastutil.ints.IntList; ++import java.io.IOException; ++import java.util.Collections; ++import java.util.HashMap; ++import java.util.LinkedHashMap; ++import java.util.Map; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++import org.objectweb.asm.ClassReader; ++import org.objectweb.asm.ClassVisitor; ++import org.objectweb.asm.Label; ++import org.objectweb.asm.MethodVisitor; ++import org.objectweb.asm.Opcodes; ++ ++@DefaultQualifier(NonNull.class) ++public enum StacktraceDeobfuscator { ++ INSTANCE; ++ ++ private final Map, Map, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) { ++ @Override ++ protected boolean removeEldestEntry(final Map.Entry, Map, IntList>> eldest) { ++ return this.size() > 127; ++ } ++ }); ++ + public void deobfuscateThrowable(final Throwable throwable) { + if (!PaperConfig.deobfuscateStacktraces) { + return; @@ -269,7 +307,8 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0 + return traceElements; + } + -+ if (this.mappings == null || traceElements.length == 0) { ++ final @Nullable Map mappings = ObfHelper.INSTANCE.mappings(); ++ if (mappings == null || traceElements.length == 0) { + return traceElements; + } + final StackTraceElement[] result = new StackTraceElement[traceElements.length]; @@ -279,21 +318,22 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0 + final String className = element.getClassName(); + final String methodName = element.getMethodName(); + -+ final ClassMapping classMapping = this.mappings.get(className); ++ final ObfHelper.ClassMapping classMapping = mappings.get(className); + if (classMapping == null) { + result[i] = element; + continue; + } + -+ final Pair nameDescriptorPair; ++ final Class clazz; + try { -+ final Class clazz = Class.forName(className); -+ nameDescriptorPair = this.determineMethodForLine(clazz, element.getLineNumber()); -+ } catch (final ReflectiveOperationException ex) { ++ clazz = Class.forName(className); ++ } catch (final ClassNotFoundException ex) { + throw new RuntimeException(ex); + } -+ -+ final MethodMapping methodMapping = classMapping.methodMappings().get(nameDescriptorPair); ++ final @Nullable Pair nameDescriptorPair = this.determineMethodForLine(clazz, element.getLineNumber()); ++ final ObfHelper.@Nullable MethodMapping methodMapping = nameDescriptorPair == null ++ ? null ++ : classMapping.methodMappings().get(nameDescriptorPair); + + result[i] = new StackTraceElement( + element.getClassLoaderName(), @@ -301,14 +341,38 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0 + element.getModuleVersion(), + classMapping.mojangName(), + methodMapping != null ? methodMapping.mojangName() : methodName, -+ this.mappedFileName(classMapping.mojangName()), ++ sourceFileName(classMapping.mojangName()), + element.getLineNumber() + ); + } + return result; + } + -+ private static @NotNull Map, IntList> buildLineMap(final @NotNull Class key) { ++ private @Nullable Pair determineMethodForLine(final Class clazz, final int lineNumber) { ++ final Map, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap); ++ for (final var entry : lineMap.entrySet()) { ++ final Pair pair = entry.getKey(); ++ final IntList lines = entry.getValue(); ++ for (int i = 0, linesSize = lines.size(); i < linesSize; i++) { ++ final int num = lines.getInt(i); ++ if (num == lineNumber) { ++ return pair; ++ } ++ } ++ } ++ return null; ++ } ++ ++ private static String sourceFileName(final String fullClassName) { ++ final int dot = fullClassName.lastIndexOf('.'); ++ final String className = dot == -1 ++ ? fullClassName ++ : fullClassName.substring(dot + 1); ++ final String rootClassName = className.split("\\$")[0]; ++ return rootClassName + ".java"; ++ } ++ ++ private static Map, IntList> buildLineMap(final Class key) { + final Map, IntList> lineMap = new HashMap<>(); + final class LineCollectingMethodVisitor extends MethodVisitor { + private final IntList lines = new IntArrayList(); @@ -347,48 +411,6 @@ index 0000000000000000000000000000000000000000..b43edcb54a50cf53b4e8b365555385d0 + } + return lineMap; + } -+ -+ private @Nullable Pair determineMethodForLine(final @NotNull Class clazz, final int lineNumber) { -+ final Map, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap); -+ for (final var entry : lineMap.entrySet()) { -+ final Pair pair = entry.getKey(); -+ final IntList lines = entry.getValue(); -+ for (int i = 0, linesSize = lines.size(); i < linesSize; i++) { -+ final int num = lines.getInt(i); -+ if (num == lineNumber) { -+ return pair; -+ } -+ } -+ } -+ return null; -+ } -+ -+ private @NotNull String mappedFileName(final @NotNull String fullClassName) { -+ final int dot = fullClassName.lastIndexOf('.'); -+ final String className = dot == -1 -+ ? fullClassName -+ : fullClassName.substring(dot + 1); -+ final String rootClassName = className.split("\\$")[0]; -+ return rootClassName + ".java"; -+ } -+ -+ public record ClassMapping( -+ String obfName, -+ String mojangName, -+ Map, MethodMapping> methodMappings -+ ) { -+ } -+ -+ public record MethodMapping( -+ String obfName, -+ String mojangName, -+ String descriptor -+ ) { -+ } -+ -+ public @Nullable Map mappings() { -+ return mappings; -+ } +} diff --git a/src/main/java/io/papermc/paper/util/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java index 2d5494d2813b773e60ddba6790b750a9a08f21f8..7695bf44503f161523ea612ef8a884ae574a2e21 100644 @@ -437,14 +459,14 @@ index 3941e14d1c3e6e688e28904948039c8b2200de5f..a4fda4a3bae9ce600e778b44cd3ef432 } } diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index db0110120f88bc06954fa8c44b2a45f0c8229574..6d7eef79de7a899ccdbc3194d925bb4caa0a4b03 100644 +index db0110120f88bc06954fa8c44b2a45f0c8229574..7b6c547e71230fbb3733f99a4597b3f5b51547b8 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -221,6 +221,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface com.destroystokyo.paper.PaperConfig.registerCommands(); com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider -+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.getClass(); // load mappings for stacktrace deobf ++ io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // load mappings for stacktrace deobf and etc. // Paper end this.setPvpAllowed(dedicatedserverproperties.pvp); diff --git a/patches/server/0730-Rate-options-and-timings-for-sensors-and-behaviors.patch b/patches/server/0730-Rate-options-and-timings-for-sensors-and-behaviors.patch index 13e80e47ac..d182f9c681 100644 --- a/patches/server/0730-Rate-options-and-timings-for-sensors-and-behaviors.patch +++ b/patches/server/0730-Rate-options-and-timings-for-sensors-and-behaviors.patch @@ -111,7 +111,7 @@ index 171321d4f1b73a25266175dfb5529dfc5cb8a5ca..f65e3b51e876f7a3d30710eed56fdca4 } diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java -index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f763e2279b5 100644 +index b1212e162ba938b3abe0df747a633ba9cbbe57c8..b1928807851a4fca53e117573b25c62a6deeb8e1 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java @@ -14,6 +14,10 @@ public abstract class Behavior { @@ -125,21 +125,22 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76 public Behavior(Map, MemoryStatus> requiredMemoryState) { this(requiredMemoryState, 60); -@@ -27,6 +31,21 @@ public abstract class Behavior { +@@ -27,6 +31,22 @@ public abstract class Behavior { this.minDuration = minRunTime; this.maxDuration = maxRunTime; this.entryCondition = requiredMemoryState; + // Paper start - configurable behavior tick rate and timings -+ Map mappings = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.mappings(); -+ String key; ++ String key = this.getClass().getSimpleName(); ++ final var mappings = io.papermc.paper.util.ObfHelper.INSTANCE.mappings(); + if (mappings != null) { -+ key = mappings.get(getClass().getName()).mojangName(); -+ int lastSeparator = key.lastIndexOf('.'); -+ if (lastSeparator != -1) { -+ key = key.substring(lastSeparator + 1); ++ final var classMapping = mappings.get(this.getClass().getName()); ++ if (classMapping != null) { ++ key = classMapping.mojangName(); ++ int lastSeparator = key.lastIndexOf('.'); ++ if (lastSeparator != -1) { ++ key = key.substring(lastSeparator + 1); ++ } + } -+ } else { -+ key = getClass().getSimpleName(); + } + this.configKey = key.toLowerCase(java.util.Locale.ROOT); + this.timing = co.aikar.timings.MinecraftTimings.getBehaviorTimings(configKey); @@ -147,7 +148,7 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76 } public Behavior.Status getStatus() { -@@ -34,11 +53,19 @@ public abstract class Behavior { +@@ -34,11 +54,19 @@ public abstract class Behavior { } public final boolean tryStart(ServerLevel world, E entity, long time) { @@ -167,7 +168,7 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76 return true; } else { return false; -@@ -49,11 +76,13 @@ public abstract class Behavior { +@@ -49,11 +77,13 @@ public abstract class Behavior { } public final void tickOrStop(ServerLevel world, E entity, long time) { @@ -182,10 +183,10 @@ index b1212e162ba938b3abe0df747a633ba9cbbe57c8..40a575cfa7670f24578f603a3e445f76 } diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java -index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..188dc5bf48d5c1f624ac117db5e79101a441370b 100644 +index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..ef0677e8a09800a5eeb30ae6575fbd5acf181a7c 100644 --- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java +++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java -@@ -19,8 +19,27 @@ public abstract class Sensor { +@@ -19,8 +19,28 @@ public abstract class Sensor { private static final TargetingConditions ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_AND_LINE_OF_SIGHT = TargetingConditions.forCombat().range(16.0D).ignoreLineOfSight().ignoreInvisibilityTesting(); private final int scanRate; private long timeToTick; @@ -196,16 +197,17 @@ index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..188dc5bf48d5c1f624ac117db5e79101 public Sensor(int senseInterval) { + // Paper start - configurable sensor tick rate and timings -+ java.util.Map mappings = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.mappings(); -+ String key; ++ String key = this.getClass().getSimpleName(); ++ final var mappings = io.papermc.paper.util.ObfHelper.INSTANCE.mappings(); + if (mappings != null) { -+ key = mappings.get(getClass().getName()).mojangName(); -+ int lastSeparator = key.lastIndexOf('.'); -+ if (lastSeparator != -1) { -+ key = key.substring(lastSeparator + 1); ++ final var classMapping = mappings.get(this.getClass().getName()); ++ if (classMapping != null) { ++ key = classMapping.mojangName(); ++ int lastSeparator = key.lastIndexOf('.'); ++ if (lastSeparator != -1) { ++ key = key.substring(lastSeparator + 1); ++ } + } -+ } else { -+ key = getClass().getSimpleName(); + } + this.configKey = key.toLowerCase(java.util.Locale.ROOT); + this.timing = co.aikar.timings.MinecraftTimings.getSensorTimings(configKey); @@ -213,7 +215,7 @@ index f94aa5147c52d2e36d6018f51b85e9dac7a6208a..188dc5bf48d5c1f624ac117db5e79101 this.scanRate = senseInterval; this.timeToTick = (long)RANDOM.nextInt(senseInterval); } -@@ -31,8 +50,12 @@ public abstract class Sensor { +@@ -31,8 +51,12 @@ public abstract class Sensor { public final void tick(ServerLevel world, E entity) { if (--this.timeToTick <= 0L) {