diff options
Diffstat (limited to 'patches/server/0015-Deobfuscate-stacktraces-in-log-messages-crash-report.patch')
-rw-r--r-- | patches/server/0015-Deobfuscate-stacktraces-in-log-messages-crash-report.patch | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/patches/server/0015-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0015-Deobfuscate-stacktraces-in-log-messages-crash-report.patch new file mode 100644 index 0000000000..8160890afe --- /dev/null +++ b/patches/server/0015-Deobfuscate-stacktraces-in-log-messages-crash-report.patch @@ -0,0 +1,581 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +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 ca7d5e2bb44e2719eee8ad046e40e1e52021a4a9..19d9cbcaa05061a5bedf5b1d821138091acfe973 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -60,6 +60,7 @@ dependencies { + mockitoAgent("org.mockito:mockito-core:5.14.1") { isTransitive = false } // Paper - configure mockito agent that is needed in newer java versions + 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 1fc859f4cc1cf552d2d578b3cda5872bc8b1015a..00bf34e1fd3593ad6d92bd292f3069cd3cbddfdd 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"> |