2021-06-21 10:09:18 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Sun, 20 Jun 2021 18:19:09 -0700
2021-06-22 06:12:07 +02:00
Subject: [PATCH] Deobfuscate stacktraces in log messages, crash reports, and
etc.
2021-06-21 10:09:18 +02:00
diff --git a/build.gradle.kts b/build.gradle.kts
2023-09-22 06:05:18 +02:00
index 7f005b9a41064f1929e8d9cb53c5ab207d3a333d..dfb43cf7be60733f3ab142de66139971bca1b1fe 100644
2021-06-21 10:09:18 +02:00
--- a/build.gradle.kts
+++ b/build.gradle.kts
2023-07-02 07:00:46 +02:00
@@ -37,6 +37,7 @@ dependencies {
2023-02-15 22:27:40 +01:00
testImplementation("org.mockito:mockito-core:4.9.0") // Paper - switch to mockito
2022-06-09 10:51:45 +02:00
implementation("org.spongepowered:configurate-yaml:4.1.2") // Paper - config files
implementation("commons-lang:commons-lang:2.6")
2021-11-05 01:23:06 +01:00
+ implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation
2023-09-22 06:05:18 +02:00
runtimeOnly("org.xerial:sqlite-jdbc:3.43.0.0")
runtimeOnly("com.mysql:mysql-connector-j:8.1.0")
2022-06-09 10:51:45 +02:00
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
2023-07-02 07:00:46 +02:00
@@ -121,6 +122,18 @@ tasks.check {
2021-06-21 10:09:18 +02:00
}
2022-06-28 00:41:59 +02:00
// Paper end
2021-06-21 10:09:18 +02:00
+// Paper start - include reobf mappings in jar for stacktrace deobfuscation
2022-10-31 23:25:30 +01:00
+val includeMappings = tasks.register<io.papermc.paperweight.tasks.IncludeMappings>("includeMappings") {
2021-06-27 05:26:17 +02:00
+ inputJar.set(tasks.fixJarForReobf.flatMap { it.outputJar })
2021-06-21 10:09:18 +02:00
+ mappings.set(tasks.reobfJar.flatMap { it.mappingsFile })
2022-10-31 23:25:30 +01:00
+ mappingsDest.set("META-INF/mappings/reobf.tiny")
2021-06-21 10:09:18 +02:00
+}
+
+tasks.reobfJar {
+ inputJar.set(includeMappings.flatMap { it.outputJar })
+}
+// Paper end - include reobf mappings in jar for stacktrace deobfuscation
+
tasks.test {
exclude("org/bukkit/craftbukkit/inventory/ItemStack*Test.class")
}
2023-07-02 07:00:46 +02:00
diff --git a/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
2021-06-21 10:09:18 +02:00
new file mode 100644
2023-07-02 07:00:46 +02:00
index 0000000000000000000000000000000000000000..66b6011ee3684695b2ab9292961c80bf2a420ee9
2021-06-21 10:09:18 +02:00
--- /dev/null
2023-07-02 07:00:46 +02:00
+++ b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
@@ -0,0 +1,66 @@
2021-06-21 10:09:18 +02:00
+package io.papermc.paper.logging;
+
2023-07-02 07:00:46 +02:00
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
2021-06-22 06:12:07 +02:00
+import org.apache.logging.log4j.core.Core;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
2021-09-30 20:05:51 +02:00
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.checkerframework.checker.nullness.qual.NonNull;
2021-06-22 06:12:07 +02:00
+
+@Plugin(
+ name = "StacktraceDeobfuscatingRewritePolicy",
+ category = Core.CATEGORY_NAME,
+ elementType = "rewritePolicy",
+ printObject = true
+)
+public final class StacktraceDeobfuscatingRewritePolicy implements RewritePolicy {
2023-07-02 07:00:46 +02:00
+ private static final MethodHandle DEOBFUSCATE_THROWABLE;
+
+ static {
+ try {
+ final Class<?> cls = Class.forName("io.papermc.paper.util.StacktraceDeobfuscator");
+ final MethodHandles.Lookup lookup = MethodHandles.lookup();
+ final VarHandle instanceHandle = lookup.findStaticVarHandle(cls, "INSTANCE", cls);
+ final Object deobfuscator = instanceHandle.get();
+ DEOBFUSCATE_THROWABLE = lookup
+ .unreflect(cls.getDeclaredMethod("deobfuscateThrowable", Throwable.class))
+ .bindTo(deobfuscator);
+ } catch (final ReflectiveOperationException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
2021-09-30 20:05:51 +02:00
+ private StacktraceDeobfuscatingRewritePolicy() {
+ }
+
2021-06-22 06:12:07 +02:00
+ @Override
2021-09-30 20:05:51 +02:00
+ public @NonNull LogEvent rewrite(final @NonNull LogEvent rewrite) {
2021-06-22 06:12:07 +02:00
+ final Throwable thrown = rewrite.getThrown();
+ if (thrown != null) {
2023-07-02 07:00:46 +02:00
+ deobfuscateThrowable(thrown);
2021-09-30 20:05:51 +02:00
+ return new Log4jLogEvent.Builder(rewrite)
+ .setThrownProxy(null)
+ .build();
2021-06-22 06:12:07 +02:00
+ }
+ return rewrite;
+ }
+
2023-07-02 07:00:46 +02:00
+ private static void deobfuscateThrowable(final Throwable thrown) {
+ try {
+ DEOBFUSCATE_THROWABLE.invoke(thrown);
+ } catch (final Error e) {
+ throw e;
+ } catch (final Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
2021-06-22 06:12:07 +02:00
+ @PluginFactory
2021-09-30 20:05:51 +02:00
+ public static @NonNull StacktraceDeobfuscatingRewritePolicy createPolicy() {
2021-06-22 06:12:07 +02:00
+ return new StacktraceDeobfuscatingRewritePolicy();
+ }
+}
2023-07-02 07:00:46 +02:00
diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
index 0bb4aaa546939b67a5d22865190f30478a9337c1..d3e619655382e50e9ac9323ed942502d85c9599c 100644
--- a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
+++ b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
@@ -91,7 +91,7 @@ public class SyncLoadFinder {
final JsonArray traces = new JsonArray();
- for (StackTraceElement element : pair.getFirst().stacktrace) {
+ for (StackTraceElement element : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(pair.getFirst().stacktrace)) {
traces.add(String.valueOf(element));
}
2021-07-20 04:22:18 +02:00
diff --git a/src/main/java/io/papermc/paper/util/ObfHelper.java b/src/main/java/io/papermc/paper/util/ObfHelper.java
2021-06-22 06:12:07 +02:00
new file mode 100644
2021-11-05 01:23:06 +01:00
index 0000000000000000000000000000000000000000..b8b17d046f836c8652ab094db00ab1af84971b2c
2021-06-22 06:12:07 +02:00
--- /dev/null
2021-07-20 04:22:18 +02:00
+++ b/src/main/java/io/papermc/paper/util/ObfHelper.java
2021-11-05 01:23:06 +01:00
@@ -0,0 +1,146 @@
2021-06-22 06:12:07 +02:00
+package io.papermc.paper.util;
+
2021-06-21 10:09:18 +02:00
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
2021-11-05 01:23:06 +01:00
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.HashSet;
2021-06-21 10:09:18 +02:00
+import java.util.Map;
2021-08-14 12:06:17 +02:00
+import java.util.Set;
2021-11-05 01:23:06 +01:00
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import net.fabricmc.mappingio.MappingReader;
+import net.fabricmc.mappingio.format.MappingFormat;
+import net.fabricmc.mappingio.tree.MappingTree;
+import net.fabricmc.mappingio.tree.MemoryMappingTree;
2021-07-20 04:22:18 +02:00
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
2021-06-21 10:09:18 +02:00
+
2021-07-20 04:22:18 +02:00
+@DefaultQualifier(NonNull.class)
+public enum ObfHelper {
2021-06-22 06:12:07 +02:00
+ INSTANCE;
+
2021-07-20 04:22:18 +02:00
+ public static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn";
+ public static final String SPIGOT_NAMESPACE = "spigot";
2021-06-21 10:09:18 +02:00
+
2021-08-14 12:06:17 +02:00
+ private final @Nullable Map<String, ClassMapping> mappingsByObfName;
+ private final @Nullable Map<String, ClassMapping> mappingsByMojangName;
2021-06-21 10:09:18 +02:00
+
2021-07-20 04:22:18 +02:00
+ ObfHelper() {
2021-08-14 12:06:17 +02:00
+ final @Nullable Set<ClassMapping> maps = loadMappingsIfPresent();
+ if (maps != null) {
2021-11-05 01:23:06 +01:00
+ this.mappingsByObfName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::obfName, map -> map));
+ this.mappingsByMojangName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::mojangName, map -> map));
2021-08-14 12:06:17 +02:00
+ } else {
+ this.mappingsByObfName = null;
+ this.mappingsByMojangName = null;
+ }
+ }
+
+ public @Nullable Map<String, ClassMapping> mappingsByObfName() {
+ return this.mappingsByObfName;
+ }
+
+ public @Nullable Map<String, ClassMapping> mappingsByMojangName() {
+ return this.mappingsByMojangName;
2021-06-21 10:09:18 +02:00
+ }
+
2021-08-14 12:06:17 +02:00
+ /**
+ * Attempts to get the obf name for a given class by its Mojang name. Will
+ * return the input string if mappings are not present.
+ *
+ * @param fullyQualifiedMojangName fully qualified class name (dotted)
+ * @return mapped or original fully qualified (dotted) class name
+ */
+ public String reobfClassName(final String fullyQualifiedMojangName) {
+ if (this.mappingsByMojangName == null) {
+ return fullyQualifiedMojangName;
+ }
+
+ final ClassMapping map = this.mappingsByMojangName.get(fullyQualifiedMojangName);
+ if (map == null) {
+ return fullyQualifiedMojangName;
+ }
+
+ return map.obfName();
+ }
+
+ /**
+ * Attempts to get the Mojang name for a given class by its obf name. Will
+ * return the input string if mappings are not present.
+ *
+ * @param fullyQualifiedObfName fully qualified class name (dotted)
+ * @return mapped or original fully qualified (dotted) class name
+ */
+ public String deobfClassName(final String fullyQualifiedObfName) {
+ if (this.mappingsByObfName == null) {
+ return fullyQualifiedObfName;
+ }
+
+ final ClassMapping map = this.mappingsByObfName.get(fullyQualifiedObfName);
+ if (map == null) {
+ return fullyQualifiedObfName;
+ }
+
+ return map.mojangName();
2021-07-20 04:22:18 +02:00
+ }
+
2021-08-14 12:06:17 +02:00
+ private static @Nullable Set<ClassMapping> loadMappingsIfPresent() {
2021-09-09 18:57:16 +02:00
+ try (final @Nullable InputStream mappingsInputStream = ObfHelper.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) {
2021-06-21 10:09:18 +02:00
+ if (mappingsInputStream == null) {
+ return null;
+ }
2021-11-05 01:23:06 +01:00
+ final MemoryMappingTree tree = new MemoryMappingTree();
+ MappingReader.read(new InputStreamReader(mappingsInputStream, StandardCharsets.UTF_8), MappingFormat.TINY_2, tree);
+ final Set<ClassMapping> classes = new HashSet<>();
+
+ final StringPool pool = new StringPool();
+ for (final MappingTree.ClassMapping cls : tree.getClasses()) {
+ final Map<String, String> methods = new HashMap<>();
+
+ for (final MappingTree.MethodMapping methodMapping : cls.getMethods()) {
+ methods.put(
+ pool.string(methodKey(
+ methodMapping.getName(SPIGOT_NAMESPACE),
+ methodMapping.getDesc(SPIGOT_NAMESPACE)
+ )),
+ pool.string(methodMapping.getName(MOJANG_PLUS_YARN_NAMESPACE))
2021-06-21 10:09:18 +02:00
+ );
+ }
+
+ final ClassMapping map = new ClassMapping(
2021-11-05 01:23:06 +01:00
+ cls.getName(SPIGOT_NAMESPACE).replace('/', '.'),
+ cls.getName(MOJANG_PLUS_YARN_NAMESPACE).replace('/', '.'),
+ Map.copyOf(methods)
2021-06-21 10:09:18 +02:00
+ );
2021-11-05 01:23:06 +01:00
+ classes.add(map);
2021-06-21 10:09:18 +02:00
+ }
+
2021-11-05 01:23:06 +01:00
+ return Set.copyOf(classes);
2021-06-21 10:09:18 +02:00
+ } catch (final IOException ex) {
+ System.err.println("Failed to load mappings for stacktrace deobfuscation.");
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
2021-11-05 01:23:06 +01:00
+ public static String methodKey(final String obfName, final String obfDescriptor) {
+ return obfName + obfDescriptor;
+ }
2021-07-20 04:22:18 +02:00
+
2021-11-05 01:23:06 +01:00
+ private static final class StringPool {
+ private final Map<String, String> pool = new HashMap<>();
+
+ public String string(final String string) {
+ return this.pool.computeIfAbsent(string, Function.identity());
+ }
+ }
+
+ public record ClassMapping(
2021-07-20 04:22:18 +02:00
+ String obfName,
+ String mojangName,
2021-11-05 01:23:06 +01:00
+ Map<String, String> methodsByObf
2021-07-20 04:22:18 +02:00
+ ) {}
+}
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
2022-06-09 10:51:45 +02:00
index 0000000000000000000000000000000000000000..eb910d4abf91488fa7cf1f5d47e0ee916c47f512
2021-07-20 04:22:18 +02:00
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
2021-11-26 07:33:08 +01:00
@@ -0,0 +1,163 @@
2021-07-20 04:22:18 +02:00
+package io.papermc.paper.util;
+
2022-06-09 10:51:45 +02:00
+import io.papermc.paper.configuration.GlobalConfiguration;
2021-07-20 04:22:18 +02:00
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.io.IOException;
2021-11-26 07:33:08 +01:00
+import java.io.InputStream;
2021-07-20 04:22:18 +02:00
+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;
+
2021-11-05 01:23:06 +01:00
+ private final Map<Class<?>, Map<String, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) {
2021-07-20 04:22:18 +02:00
+ @Override
2021-11-05 01:23:06 +01:00
+ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Map<String, IntList>> eldest) {
2021-07-20 04:22:18 +02:00
+ return this.size() > 127;
+ }
+ });
+
2021-06-22 06:12:07 +02:00
+ public void deobfuscateThrowable(final Throwable throwable) {
2022-06-09 10:51:45 +02:00
+ if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
2021-06-22 06:12:07 +02:00
+ return;
2021-06-21 10:09:18 +02:00
+ }
+
+ throwable.setStackTrace(this.deobfuscateStacktrace(throwable.getStackTrace()));
+ final Throwable cause = throwable.getCause();
+ if (cause != null) {
+ this.deobfuscateThrowable(cause);
+ }
+ for (final Throwable suppressed : throwable.getSuppressed()) {
+ this.deobfuscateThrowable(suppressed);
+ }
+ }
+
+ public StackTraceElement[] deobfuscateStacktrace(final StackTraceElement[] traceElements) {
2022-06-09 10:51:45 +02:00
+ if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
2021-06-22 06:12:07 +02:00
+ return traceElements;
+ }
+
2021-08-14 12:06:17 +02:00
+ final @Nullable Map<String, ObfHelper.ClassMapping> mappings = ObfHelper.INSTANCE.mappingsByObfName();
2021-07-20 04:22:18 +02:00
+ if (mappings == null || traceElements.length == 0) {
2021-06-21 10:09:18 +02:00
+ return traceElements;
+ }
+ final StackTraceElement[] result = new StackTraceElement[traceElements.length];
+ for (int i = 0; i < traceElements.length; i++) {
+ final StackTraceElement element = traceElements[i];
+
+ final String className = element.getClassName();
+ final String methodName = element.getMethodName();
+
2021-07-20 04:22:18 +02:00
+ final ObfHelper.ClassMapping classMapping = mappings.get(className);
2021-06-21 10:09:18 +02:00
+ if (classMapping == null) {
+ result[i] = element;
+ continue;
+ }
+
2021-07-20 04:22:18 +02:00
+ final Class<?> clazz;
2021-06-21 10:09:18 +02:00
+ try {
2021-07-20 04:22:18 +02:00
+ clazz = Class.forName(className);
+ } catch (final ClassNotFoundException ex) {
2021-06-21 10:09:18 +02:00
+ throw new RuntimeException(ex);
+ }
2021-11-05 01:23:06 +01:00
+ final @Nullable String methodKey = this.determineMethodForLine(clazz, element.getLineNumber());
+ final @Nullable String mappedMethodName = methodKey == null ? null : classMapping.methodsByObf().get(methodKey);
2021-06-21 10:09:18 +02:00
+
+ result[i] = new StackTraceElement(
+ element.getClassLoaderName(),
+ element.getModuleName(),
+ element.getModuleVersion(),
+ classMapping.mojangName(),
2021-11-05 01:23:06 +01:00
+ mappedMethodName != null ? mappedMethodName : methodName,
2021-07-20 04:22:18 +02:00
+ sourceFileName(classMapping.mojangName()),
2021-06-21 10:09:18 +02:00
+ element.getLineNumber()
+ );
+ }
+ return result;
+ }
+
2021-11-05 01:23:06 +01:00
+ private @Nullable String determineMethodForLine(final Class<?> clazz, final int lineNumber) {
+ final Map<String, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap);
2021-07-20 04:22:18 +02:00
+ for (final var entry : lineMap.entrySet()) {
2021-11-05 01:23:06 +01:00
+ final String methodKey = entry.getKey();
2021-07-20 04:22:18 +02:00
+ final IntList lines = entry.getValue();
+ for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
+ final int num = lines.getInt(i);
+ if (num == lineNumber) {
2021-11-05 01:23:06 +01:00
+ return methodKey;
2021-07-20 04:22:18 +02:00
+ }
+ }
+ }
+ 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";
+ }
+
2021-11-05 01:23:06 +01:00
+ private static Map<String, IntList> buildLineMap(final Class<?> key) {
+ final Map<String, IntList> lineMap = new HashMap<>();
2021-06-21 10:09:18 +02:00
+ final class LineCollectingMethodVisitor extends MethodVisitor {
+ private final IntList lines = new IntArrayList();
+ private final String name;
+ private final String descriptor;
+
+ LineCollectingMethodVisitor(String name, String descriptor) {
+ super(Opcodes.ASM9);
+ this.name = name;
+ this.descriptor = descriptor;
+ }
+
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ super.visitLineNumber(line, start);
+ this.lines.add(line);
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
2021-11-05 01:23:06 +01:00
+ lineMap.put(ObfHelper.methodKey(this.name, this.descriptor), this.lines);
2021-06-21 10:09:18 +02:00
+ }
+ }
+ final ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
+ return new LineCollectingMethodVisitor(name, descriptor);
+ }
+ };
+ try {
2021-11-26 07:33:08 +01:00
+ final @Nullable InputStream inputStream = StacktraceDeobfuscator.class.getClassLoader()
+ .getResourceAsStream(key.getName().replace('.', '/') + ".class");
+ if (inputStream == null) {
+ throw new IllegalStateException("Could not find class file: " + key.getName());
+ }
+ final byte[] classData;
+ try (inputStream) {
+ classData = inputStream.readAllBytes();
+ }
+ final ClassReader reader = new ClassReader(classData);
2021-06-21 10:09:18 +02:00
+ reader.accept(classVisitor, 0);
+ } catch (final IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ return lineMap;
+ }
+}
2021-06-22 06:12:07 +02:00
diff --git a/src/main/java/io/papermc/paper/util/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java
2022-10-19 04:21:07 +02:00
index 2d5494d2813b773e60ddba6790b750a9a08f21f8..0b210bdf7c1f5962afbd44195af6f84f625635e3 100644
2021-06-22 06:12:07 +02:00
--- a/src/main/java/io/papermc/paper/util/TraceUtil.java
+++ b/src/main/java/io/papermc/paper/util/TraceUtil.java
2022-10-19 04:21:07 +02:00
@@ -6,13 +6,20 @@ public final class TraceUtil {
2021-06-22 06:12:07 +02:00
public static void dumpTraceForThread(Thread thread, String reason) {
Bukkit.getLogger().warning(thread.getName() + ": " + reason);
- StackTraceElement[] trace = thread.getStackTrace();
+ StackTraceElement[] trace = StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace());
for (StackTraceElement traceElement : trace) {
Bukkit.getLogger().warning("\tat " + traceElement);
}
}
public static void dumpTraceForThread(String reason) {
- new Throwable(reason).printStackTrace();
+ final Throwable throwable = new Throwable(reason);
+ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(throwable);
+ throwable.printStackTrace();
2022-10-19 04:21:07 +02:00
+ }
+
+ public static void printStackTrace(Throwable thr) {
+ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
+ thr.printStackTrace();
2021-06-22 06:12:07 +02:00
}
}
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
2023-09-22 06:05:18 +02:00
index 226cba0c1eeedd9e80acd603c46b802c183db1fa..1d714d3eff11ed14f218656008190017494d4830 100644
2021-06-22 06:12:07 +02:00
--- a/src/main/java/net/minecraft/CrashReport.java
+++ b/src/main/java/net/minecraft/CrashReport.java
2022-07-27 22:17:18 +02:00
@@ -32,6 +32,7 @@ public class CrashReport {
2021-06-22 06:12:07 +02:00
private final SystemReport systemReport = new SystemReport();
public CrashReport(String message, Throwable cause) {
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(cause); // Paper
this.title = message;
this.exception = cause;
this.systemReport.setDetail("CraftBukkit Information", new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit
2021-07-04 12:20:11 +02:00
diff --git a/src/main/java/net/minecraft/CrashReportCategory.java b/src/main/java/net/minecraft/CrashReportCategory.java
2023-09-22 19:31:02 +02:00
index 9cbe28043a1105a2a46df7a784f02aeb95ab4a6e..ec82643b3d731089cbc622c92bbd01131efd4fea 100644
2021-07-04 12:20:11 +02:00
--- a/src/main/java/net/minecraft/CrashReportCategory.java
+++ b/src/main/java/net/minecraft/CrashReportCategory.java
@@ -104,6 +104,7 @@ public class CrashReportCategory {
} else {
this.stackTrace = new StackTraceElement[stackTraceElements.length - 3 - ignoredCallCount];
System.arraycopy(stackTraceElements, 3 + ignoredCallCount, this.stackTrace, 0, this.stackTrace.length);
+ this.stackTrace = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(this.stackTrace); // Paper
return this.stackTrace.length;
}
}
2021-12-23 11:32:26 +01:00
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
2023-09-22 06:05:18 +02:00
index 62df5c51fa11f85ec2fad20c0ea5aa6241294e8a..81e49a770fd8a10e987f3e67a5ab99fc39d5e47f 100644
2021-12-23 11:32:26 +01:00
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
2023-09-22 06:05:18 +02:00
@@ -75,13 +75,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public static final AttributeKey<ConnectionProtocol.CodecData<?>> ATTRIBUTE_SERVERBOUND_PROTOCOL = AttributeKey.valueOf("serverbound_protocol");
public static final AttributeKey<ConnectionProtocol.CodecData<?>> ATTRIBUTE_CLIENTBOUND_PROTOCOL = AttributeKey.valueOf("clientbound_protocol");
public static final Supplier<NioEventLoopGroup> NETWORK_WORKER_GROUP = Suppliers.memoize(() -> {
2021-12-23 11:32:26 +01:00
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
});
2023-09-22 06:05:18 +02:00
public static final Supplier<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = Suppliers.memoize(() -> {
2021-12-23 11:32:26 +01:00
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
});
2023-09-22 06:05:18 +02:00
public static final Supplier<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = Suppliers.memoize(() -> {
2021-12-23 11:32:26 +01:00
- return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
+ return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
});
private final PacketFlow receiving;
2023-09-22 06:05:18 +02:00
private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
@@ -195,7 +195,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
2022-10-19 04:21:07 +02:00
}
}
- if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot
+ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) io.papermc.paper.util.TraceUtil.printStackTrace(throwable); // Spigot // Paper
}
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
2021-06-22 06:12:07 +02:00
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
2023-09-22 06:05:18 +02:00
index f72c1a4baff6ac9a3b1df83ece3925e2b1678df9..967817ad15146407668361a85c89be6beb6a73f9 100644
2021-06-22 06:12:07 +02:00
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
2022-12-07 21:16:54 +01:00
@@ -195,6 +195,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
2022-06-09 10:51:45 +02:00
org.spigotmc.SpigotConfig.registerCommands();
// Spigot end
// Paper start
+ io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc.
paperConfigurations.initializeGlobalConfiguration();
paperConfigurations.initializeWorldDefaultsConfiguration();
org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash);
2022-10-19 04:21:07 +02:00
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
2023-09-22 14:33:54 +02:00
index 4307b72acc389ccab97b2b7f3f824ddeca5a3cda..6bb7f2050e93f63045ea608672751da28138d3f8 100644
2022-10-19 04:21:07 +02:00
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
2023-06-08 00:58:10 +02:00
@@ -221,7 +221,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
2022-10-19 04:21:07 +02:00
public final UUID uuid;
public boolean hasPhysicsEvent = true; // Paper
public static Throwable getAddToWorldStackTrace(Entity entity) {
- return new Throwable(entity + " Added to world at " + new java.util.Date());
+ final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date());
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
+ return thr;
}
@Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
2023-09-22 06:05:18 +02:00
@@ -1235,7 +1237,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
2022-10-19 04:21:07 +02:00
if (entity.isRemoved()) {
// Paper start
if (DEBUG_ENTITIES) {
- new Throwable("Tried to add entity " + entity + " but it was marked as removed already").printStackTrace(); // CraftBukkit
+ io.papermc.paper.util.TraceUtil.dumpTraceForThread("Tried to add entity " + entity + " but it was marked as removed already"); // CraftBukkit
getAddToWorldStackTrace(entity).printStackTrace();
}
// Paper end
2023-09-22 06:05:18 +02:00
diff --git a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
index 496ac1d2fd43c5ddfe7fa7a8b72a75860ec43443..6c2a5b2cb57fe799cc1c5002662894ad6e2b22aa 100644
--- a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -132,7 +132,7 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
// Paper start
if (MinecraftServer.getServer().isDebugging()) {
- exception.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception);
}
// Paper end
this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
2021-12-23 11:32:26 +01:00
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
2023-09-22 06:05:18 +02:00
index d870fbec236a3660f12e0f45cf9431067b18468b..c98d3c5afcce17a6f13f8d456b9f77b1f2a4f3f0 100644
2021-12-23 11:32:26 +01:00
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
2023-09-22 06:05:18 +02:00
@@ -52,10 +52,10 @@ public class ServerConnectionListener {
2021-12-23 11:32:26 +01:00
2022-03-01 06:43:03 +01:00
private static final Logger LOGGER = LogUtils.getLogger();
2023-09-22 06:05:18 +02:00
public static final Supplier<NioEventLoopGroup> SERVER_EVENT_GROUP = Suppliers.memoize(() -> {
2021-12-23 11:32:26 +01:00
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build());
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
});
2023-09-22 06:05:18 +02:00
public static final Supplier<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = Suppliers.memoize(() -> {
2021-12-23 11:32:26 +01:00
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build());
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
});
final MinecraftServer server;
public volatile boolean running;
2022-10-19 04:21:07 +02:00
diff --git a/src/main/java/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
2023-09-22 06:05:18 +02:00
index 5db27d7bcaaa2eeaeeb08401513d8d23f6cb63c7..ce43cb0152ba07c6c21e08142d65813d47c3b63b 100644
2022-10-19 04:21:07 +02:00
--- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java
+++ b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
2023-09-22 06:05:18 +02:00
@@ -359,7 +359,7 @@ public class OldUsersConverter {
2022-10-19 04:21:07 +02:00
try {
root = NbtIo.readCompressed(new java.io.FileInputStream(file5));
} catch (Exception exception) {
- exception.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
ServerInternalException.reportInternalException(exception); // Paper
}
2023-09-22 06:05:18 +02:00
@@ -373,7 +373,7 @@ public class OldUsersConverter {
2022-10-19 04:21:07 +02:00
try {
NbtIo.writeCompressed(root, new java.io.FileOutputStream(file2));
} catch (Exception exception) {
- exception.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
ServerInternalException.reportInternalException(exception); // Paper
}
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
2023-09-22 06:05:18 +02:00
index c9a6a56dfd25601bc0a11e5ddb349dba372c0e18..9cb4c145a36de0fe5be148b854ebb6f2a8446f49 100644
2022-10-19 04:21:07 +02:00
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
2023-06-08 00:58:10 +02:00
@@ -603,7 +603,7 @@ public class LevelChunk extends ChunkAccess {
2022-10-19 04:21:07 +02:00
+ " (" + getBlockState(blockposition) + ") where there was no entity tile!\n" +
"Chunk coordinates: " + (this.chunkPos.x * 16) + "," + (this.chunkPos.z * 16) +
"\nWorld: " + level.getLevel().dimension().location());
- e.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(e);
ServerInternalException.reportInternalException(e);
// Paper end
// CraftBukkit end
2021-12-23 11:32:26 +01:00
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
index 3c1992e212a6d6f1db4d5b807b38d71913619fc0..9c1aff17aabd062640e3f451a2ef8c50a7c62f10 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
@@ -40,9 +40,9 @@ public class CraftAsyncScheduler extends CraftScheduler {
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, Integer.MAX_VALUE,30L, TimeUnit.SECONDS, new SynchronousQueue<>(),
- new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build());
+ new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
private final Executor management = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
- .setNameFormat("Craft Async Scheduler Management Thread").build());
+ .setNameFormat("Craft Async Scheduler Management Thread").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
private final List<CraftTask> temp = new ArrayList<>();
CraftAsyncScheduler() {
2021-06-22 06:12:07 +02:00
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
2023-09-22 06:05:18 +02:00
index e71041d41eae31cce73d8817c4f95f16c8c600aa..71dbda5ae954b31ca3915de8823b552a09dfcab8 100644
2021-06-22 06:12:07 +02:00
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
2023-09-22 06:05:18 +02:00
@@ -105,7 +105,7 @@ public class WatchdogThread extends Thread
2021-06-22 06:12:07 +02:00
log.log( Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity" );
log.log( Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated" );
log.log( Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
- for ( StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace() )
+ for ( StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) ) // Paper
{
log.log( Level.SEVERE, "\t\t" + stack );
}
2023-09-22 06:05:18 +02:00
@@ -191,7 +191,7 @@ public class WatchdogThread extends Thread
2021-06-22 06:12:07 +02:00
}
log.log( Level.SEVERE, "\tStack:" );
//
- for ( StackTraceElement stack : thread.getStackTrace() )
+ for ( StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace()) ) // Paper
{
log.log( Level.SEVERE, "\t\t" + stack );
}
2021-06-21 10:09:18 +02:00
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
2023-06-13 00:00:12 +02:00
index 266b4e6fb3988b5848021c83fdc68e342c70b188..2b247d55e39246fbef31279b14c45fc40f956bfb 100644
2021-06-21 10:09:18 +02:00
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
2021-08-12 19:55:20 +02:00
@@ -30,10 +30,14 @@
2021-06-21 10:09:18 +02:00
<DefaultRolloverStrategy max="1000"/>
</RollingRandomAccessFile>
2021-08-12 19:55:20 +02:00
<Async name="Async">
+ <AppenderRef ref="rewrite"/>
+ </Async>
2021-06-21 10:09:18 +02:00
+ <Rewrite name="rewrite">
+ <StacktraceDeobfuscatingRewritePolicy />
2021-08-12 19:55:20 +02:00
<AppenderRef ref="File"/>
<AppenderRef ref="TerminalConsole" level="info"/>
<AppenderRef ref="ServerGuiConsole" level="info"/>
- </Async>
2021-06-21 10:09:18 +02:00
+ </Rewrite>
</Appenders>
<Loggers>
<Root level="info">