mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-30 04:02:50 +01:00
581 lines
28 KiB
Diff
581 lines
28 KiB
Diff
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
|
|
Subject: [PATCH] Deobfuscate stacktraces in log messages, crash reports, and
|
|
etc.
|
|
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index b36f2e9e9980f6d2596c57791e0769c6ce734d11..c73ab51caa985089ade4df0616ee8d3333632980 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -46,6 +46,7 @@ dependencies {
|
|
testImplementation("org.mockito:mockito-core:5.14.1")
|
|
testImplementation("org.ow2.asm:asm-tree:9.7.1")
|
|
testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") // Paper - CartesianTest
|
|
+ implementation("net.neoforged:srgutils:1.0.9") // Paper - mappings handling
|
|
}
|
|
|
|
paperweight {
|
|
diff --git a/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..66b6011ee3684695b2ab9292961c80bf2a420ee9
|
|
--- /dev/null
|
|
+++ b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
|
|
@@ -0,0 +1,66 @@
|
|
+package io.papermc.paper.logging;
|
|
+
|
|
+import java.lang.invoke.MethodHandle;
|
|
+import java.lang.invoke.MethodHandles;
|
|
+import java.lang.invoke.VarHandle;
|
|
+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;
|
|
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
|
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
|
+
|
|
+@Plugin(
|
|
+ name = "StacktraceDeobfuscatingRewritePolicy",
|
|
+ category = Core.CATEGORY_NAME,
|
|
+ elementType = "rewritePolicy",
|
|
+ printObject = true
|
|
+)
|
|
+public final class StacktraceDeobfuscatingRewritePolicy implements RewritePolicy {
|
|
+ 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);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private StacktraceDeobfuscatingRewritePolicy() {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NonNull LogEvent rewrite(final @NonNull LogEvent rewrite) {
|
|
+ final Throwable thrown = rewrite.getThrown();
|
|
+ if (thrown != null) {
|
|
+ deobfuscateThrowable(thrown);
|
|
+ return new Log4jLogEvent.Builder(rewrite)
|
|
+ .setThrownProxy(null)
|
|
+ .build();
|
|
+ }
|
|
+ return rewrite;
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @PluginFactory
|
|
+ public static @NonNull StacktraceDeobfuscatingRewritePolicy createPolicy() {
|
|
+ return new StacktraceDeobfuscatingRewritePolicy();
|
|
+ }
|
|
+}
|
|
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..9e6d48335b37fa5204bfebf396d748089884555b
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/util/ObfHelper.java
|
|
@@ -0,0 +1,156 @@
|
|
+package io.papermc.paper.util;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.io.InputStream;
|
|
+import java.util.HashMap;
|
|
+import java.util.HashSet;
|
|
+import java.util.Map;
|
|
+import java.util.Objects;
|
|
+import java.util.Set;
|
|
+import java.util.stream.Collectors;
|
|
+import net.neoforged.srgutils.IMappingFile;
|
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
|
+
|
|
+@DefaultQualifier(NonNull.class)
|
|
+public enum ObfHelper {
|
|
+ INSTANCE;
|
|
+
|
|
+ private final @Nullable Map<String, ClassMapping> mappingsByObfName;
|
|
+ private final @Nullable Map<String, ClassMapping> mappingsByMojangName;
|
|
+
|
|
+ ObfHelper() {
|
|
+ final @Nullable Set<ClassMapping> maps = loadMappingsIfPresent();
|
|
+ if (maps != null) {
|
|
+ this.mappingsByObfName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::obfName, map -> map));
|
|
+ this.mappingsByMojangName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::mojangName, map -> map));
|
|
+ } 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;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * 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();
|
|
+ }
|
|
+
|
|
+ private static @Nullable Set<ClassMapping> loadMappingsIfPresent() {
|
|
+ try (final @Nullable InputStream mappingsInputStream = ObfHelper.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) {
|
|
+ if (mappingsInputStream == null) {
|
|
+ return null;
|
|
+ }
|
|
+ final IMappingFile mappings = IMappingFile.load(mappingsInputStream); // Mappings are mojang->spigot
|
|
+ final Set<ClassMapping> classes = new HashSet<>();
|
|
+
|
|
+ final StringPool pool = new StringPool();
|
|
+ for (final IMappingFile.IClass cls : mappings.getClasses()) {
|
|
+ final Map<String, String> methods = new HashMap<>();
|
|
+ final Map<String, String> fields = new HashMap<>();
|
|
+ final Map<String, String> strippedMethods = new HashMap<>();
|
|
+
|
|
+ for (final IMappingFile.IMethod methodMapping : cls.getMethods()) {
|
|
+ methods.put(
|
|
+ pool.string(methodKey(
|
|
+ Objects.requireNonNull(methodMapping.getMapped()),
|
|
+ Objects.requireNonNull(methodMapping.getMappedDescriptor())
|
|
+ )),
|
|
+ pool.string(Objects.requireNonNull(methodMapping.getOriginal()))
|
|
+ );
|
|
+
|
|
+ strippedMethods.put(
|
|
+ pool.string(pool.string(strippedMethodKey(
|
|
+ methodMapping.getMapped(),
|
|
+ methodMapping.getDescriptor()
|
|
+ ))),
|
|
+ pool.string(methodMapping.getOriginal())
|
|
+ );
|
|
+ }
|
|
+ for (final IMappingFile.IField field : cls.getFields()) {
|
|
+ fields.put(
|
|
+ pool.string(field.getMapped()),
|
|
+ pool.string(field.getOriginal())
|
|
+ );
|
|
+ }
|
|
+
|
|
+ final ClassMapping map = new ClassMapping(
|
|
+ Objects.requireNonNull(cls.getMapped()).replace('/', '.'),
|
|
+ Objects.requireNonNull(cls.getOriginal()).replace('/', '.'),
|
|
+ Map.copyOf(methods),
|
|
+ Map.copyOf(fields),
|
|
+ Map.copyOf(strippedMethods)
|
|
+ );
|
|
+ classes.add(map);
|
|
+ }
|
|
+
|
|
+ return Set.copyOf(classes);
|
|
+ } catch (final IOException ex) {
|
|
+ System.err.println("Failed to load mappings.");
|
|
+ ex.printStackTrace();
|
|
+ return null;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static String strippedMethodKey(final String methodName, final String methodDescriptor) {
|
|
+ final String methodKey = methodKey(methodName, methodDescriptor);
|
|
+ final int returnDescriptorEnd = methodKey.indexOf(')');
|
|
+ return methodKey.substring(0, returnDescriptorEnd + 1);
|
|
+ }
|
|
+
|
|
+ public static String methodKey(final String methodName, final String methodDescriptor) {
|
|
+ return methodName + methodDescriptor;
|
|
+ }
|
|
+
|
|
+ public record ClassMapping(
|
|
+ String obfName,
|
|
+ String mojangName,
|
|
+ Map<String, String> methodsByObf,
|
|
+ Map<String, String> fieldsByObf,
|
|
+ // obf name with mapped desc to mapped name. return value is excluded from desc as reflection doesn't use it
|
|
+ Map<String, String> strippedMethods
|
|
+ ) {}
|
|
+}
|
|
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..242811578a786e3807a1a7019d472d5a68f87116
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
|
@@ -0,0 +1,144 @@
|
|
+package io.papermc.paper.util;
|
|
+
|
|
+import io.papermc.paper.configuration.GlobalConfiguration;
|
|
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
+import java.io.IOException;
|
|
+import java.io.InputStream;
|
|
+import java.util.Collections;
|
|
+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<Class<?>, Int2ObjectMap<String>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) {
|
|
+ @Override
|
|
+ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Int2ObjectMap<String>> eldest) {
|
|
+ return this.size() > 127;
|
|
+ }
|
|
+ });
|
|
+
|
|
+ public void deobfuscateThrowable(final Throwable throwable) {
|
|
+ if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ 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) {
|
|
+ if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
|
|
+ return traceElements;
|
|
+ }
|
|
+
|
|
+ final @Nullable Map<String, ObfHelper.ClassMapping> mappings = ObfHelper.INSTANCE.mappingsByObfName();
|
|
+ if (mappings == null || traceElements.length == 0) {
|
|
+ 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();
|
|
+
|
|
+ final ObfHelper.ClassMapping classMapping = mappings.get(className);
|
|
+ if (classMapping == null) {
|
|
+ result[i] = element;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final Class<?> clazz;
|
|
+ try {
|
|
+ clazz = Class.forName(className);
|
|
+ } catch (final ClassNotFoundException ex) {
|
|
+ throw new RuntimeException(ex);
|
|
+ }
|
|
+ final @Nullable String methodKey = this.determineMethodForLine(clazz, element.getLineNumber());
|
|
+ final @Nullable String mappedMethodName = methodKey == null ? null : classMapping.methodsByObf().get(methodKey);
|
|
+
|
|
+ result[i] = new StackTraceElement(
|
|
+ element.getClassLoaderName(),
|
|
+ element.getModuleName(),
|
|
+ element.getModuleVersion(),
|
|
+ classMapping.mojangName(),
|
|
+ mappedMethodName != null ? mappedMethodName : methodName,
|
|
+ sourceFileName(classMapping.mojangName()),
|
|
+ element.getLineNumber()
|
|
+ );
|
|
+ }
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ private @Nullable String determineMethodForLine(final Class<?> clazz, final int lineNumber) {
|
|
+ return this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap).get(lineNumber);
|
|
+ }
|
|
+
|
|
+ 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 Int2ObjectMap<String> buildLineMap(final Class<?> key) {
|
|
+ final StringPool pool = new StringPool();
|
|
+ final Int2ObjectMap<String> lineMap = new Int2ObjectOpenHashMap<>();
|
|
+ final class LineCollectingMethodVisitor extends MethodVisitor {
|
|
+ private final String name;
|
|
+ private final String descriptor;
|
|
+
|
|
+ LineCollectingMethodVisitor(final String name, final String descriptor) {
|
|
+ super(Opcodes.ASM9);
|
|
+ this.name = name;
|
|
+ this.descriptor = descriptor;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void visitLineNumber(final int line, final Label start) {
|
|
+ lineMap.put(line, pool.string(ObfHelper.methodKey(this.name, this.descriptor)));
|
|
+ }
|
|
+ }
|
|
+ final ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9) {
|
|
+ @Override
|
|
+ public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) {
|
|
+ return new LineCollectingMethodVisitor(name, descriptor);
|
|
+ }
|
|
+ };
|
|
+ try {
|
|
+ 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);
|
|
+ reader.accept(classVisitor, 0);
|
|
+ } catch (final IOException ex) {
|
|
+ throw new RuntimeException(ex);
|
|
+ }
|
|
+ return lineMap;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/util/StringPool.java b/src/main/java/io/papermc/paper/util/StringPool.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c0a486cb46ff30353c3ff09567891cd36238eeb4
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/util/StringPool.java
|
|
@@ -0,0 +1,34 @@
|
|
+package io.papermc.paper.util;
|
|
+
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+import java.util.function.Function;
|
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
|
+
|
|
+/**
|
|
+ * De-duplicates {@link String} instances without using {@link String#intern()}.
|
|
+ *
|
|
+ * <p>Interning may not be desired as we may want to use the heap for our pool,
|
|
+ * so it can be garbage collected as normal, etc.</p>
|
|
+ *
|
|
+ * <p>Additionally, interning can be slow due to the potentially large size of the
|
|
+ * pool (as it is shared for the entire JVM), and because most JVMs implement
|
|
+ * it using JNI.</p>
|
|
+ */
|
|
+@DefaultQualifier(NonNull.class)
|
|
+public final class StringPool {
|
|
+ private final Map<String, String> pool;
|
|
+
|
|
+ public StringPool() {
|
|
+ this(new HashMap<>());
|
|
+ }
|
|
+
|
|
+ public StringPool(final Map<String, String> map) {
|
|
+ this.pool = map;
|
|
+ }
|
|
+
|
|
+ public String string(final String string) {
|
|
+ return this.pool.computeIfAbsent(string, Function.identity());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
|
|
index 1938ae691dafec1fc1e5a68792d1191bd52b4e5c..268310642181a715815d3b2d1c0f090e6252971a 100644
|
|
--- a/src/main/java/net/minecraft/CrashReport.java
|
|
+++ b/src/main/java/net/minecraft/CrashReport.java
|
|
@@ -34,6 +34,7 @@ public class CrashReport {
|
|
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
|
|
diff --git a/src/main/java/net/minecraft/CrashReportCategory.java b/src/main/java/net/minecraft/CrashReportCategory.java
|
|
index 81831a061d7fbf513f4aa7880e3b18ef3fdc05d7..1e9873d7b258ce1f0b2437cb1e487157a16f6834 100644
|
|
--- a/src/main/java/net/minecraft/CrashReportCategory.java
|
|
+++ b/src/main/java/net/minecraft/CrashReportCategory.java
|
|
@@ -110,6 +110,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;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
|
index 77985072928a1b892fb4f7dec1d0899324780082..f5e6610d271ef2c997fb3d1a5f65e0bf0740805a 100644
|
|
--- a/src/main/java/net/minecraft/network/Connection.java
|
|
+++ b/src/main/java/net/minecraft/network/Connection.java
|
|
@@ -82,13 +82,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
marker.add(Connection.PACKET_MARKER);
|
|
});
|
|
public static final Supplier<NioEventLoopGroup> NETWORK_WORKER_GROUP = Suppliers.memoize(() -> {
|
|
- 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
|
|
});
|
|
public static final Supplier<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = Suppliers.memoize(() -> {
|
|
- 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
|
|
});
|
|
public static final Supplier<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = Suppliers.memoize(() -> {
|
|
- 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 static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
|
|
private final PacketFlow receiving;
|
|
@@ -197,7 +197,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
|
|
}
|
|
}
|
|
- 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) {
|
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
index dcb9258d3fbdfdfd41065d4c0919ed4300eac3ae..a61a92078a8bb4979f231c02ef5aa990b8ab57ad 100644
|
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
|
@@ -209,6 +209,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
|
org.spigotmc.SpigotConfig.init((java.io.File) this.options.valueOf("spigot-settings"));
|
|
org.spigotmc.SpigotConfig.registerCommands();
|
|
// Spigot end
|
|
+ io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc.
|
|
// Paper start - initialize global and world-defaults configuration
|
|
this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
|
|
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
|
index 987360a266a5a870f06929b00c9f451901188fd6..2cf3e79ec5e8706b71d27ebad4668773f0b91195 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
|
@@ -52,10 +52,10 @@ public class ServerConnectionListener {
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final Supplier<NioEventLoopGroup> SERVER_EVENT_GROUP = Suppliers.memoize(() -> {
|
|
- 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
|
|
});
|
|
public static final Supplier<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = Suppliers.memoize(() -> {
|
|
- 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;
|
|
diff --git a/src/main/java/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
|
|
index 8d06e8d286da2573e40794adab695ff77e5afd86..68551947f5b7d3471f15bd74ccd86519ab34c1c1 100644
|
|
--- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java
|
|
+++ b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
|
|
@@ -356,7 +356,7 @@ public class OldUsersConverter {
|
|
try {
|
|
root = NbtIo.readCompressed(new java.io.FileInputStream(file5), NbtAccounter.unlimitedHeap());
|
|
} catch (Exception exception) {
|
|
- exception.printStackTrace();
|
|
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
|
|
}
|
|
|
|
if (root != null) {
|
|
@@ -369,7 +369,7 @@ public class OldUsersConverter {
|
|
try {
|
|
NbtIo.writeCompressed(root, new java.io.FileOutputStream(file2));
|
|
} catch (Exception exception) {
|
|
- exception.printStackTrace();
|
|
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
|
|
}
|
|
}
|
|
// CraftBukkit end
|
|
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
index c4bf7053d83d207caca0e13e19f5c1afa7062de3..f697d45e0ac4e9cdc8a46121510a04c0f294d91f 100644
|
|
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
|
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
|
@@ -130,7 +130,7 @@ public class WatchdogThread extends Thread
|
|
}
|
|
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 );
|
|
}
|
|
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
|
index 18e961a37b2830da6e5dab7aa35116b2f5215898..128fa1376f22d3429a23d79a2772abf2e7fec2bc 100644
|
|
--- a/src/main/resources/log4j2.xml
|
|
+++ b/src/main/resources/log4j2.xml
|
|
@@ -30,10 +30,14 @@
|
|
<DefaultRolloverStrategy max="1000"/>
|
|
</RollingRandomAccessFile>
|
|
<Async name="Async">
|
|
+ <AppenderRef ref="rewrite"/>
|
|
+ </Async>
|
|
+ <Rewrite name="rewrite">
|
|
+ <StacktraceDeobfuscatingRewritePolicy />
|
|
<AppenderRef ref="File"/>
|
|
<AppenderRef ref="TerminalConsole" level="info"/>
|
|
<AppenderRef ref="ServerGuiConsole" level="info"/>
|
|
- </Async>
|
|
+ </Rewrite>
|
|
</Appenders>
|
|
<Loggers>
|
|
<Root level="info">
|