diff options
Diffstat (limited to 'patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch')
-rw-r--r-- | patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch new file mode 100644 index 0000000000..5968dd9be8 --- /dev/null +++ b/patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch @@ -0,0 +1,611 @@ +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 e260462933a9f7065b2360e6bf9e4ee56069a705..a1a8c4778742584125d6084fa761b1bc86f6a842 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -1,4 +1,6 @@ ++import io.papermc.paperweight.tasks.BaseTask + import io.papermc.paperweight.util.* ++import java.nio.file.Files + + plugins { + java +@@ -19,12 +21,14 @@ dependencies { + Scanning takes about 1-2 seconds so adding this speeds up the server start. + */ + implementation("org.apache.logging.log4j:log4j-core:2.14.1") // Paper - implementation ++ annotationProcessor("org.apache.logging.log4j:log4j-core:2.14.1") // Paper - Needed to generate meta for our Log4j plugins + // Paper end + implementation("org.apache.logging.log4j:log4j-iostreams:2.17.1") // Paper + implementation("org.ow2.asm:asm:9.3") + implementation("org.ow2.asm:asm-commons:9.3") // Paper - ASM event executor generation + implementation("org.spongepowered:configurate-yaml:4.1.2") // Paper - config files + implementation("commons-lang:commons-lang:2.6") ++ implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation + runtimeOnly("org.xerial:sqlite-jdbc:3.36.0.3") + runtimeOnly("mysql:mysql-connector-java:8.0.29") + runtimeOnly("com.lmax:disruptor:3.4.4") // Paper +@@ -104,6 +108,45 @@ tasks.check { + } + // Paper end + ++// Paper start - include reobf mappings in jar for stacktrace deobfuscation ++abstract class IncludeMappings : BaseTask() { ++ @get:InputFile ++ abstract val inputJar: RegularFileProperty ++ ++ @get:InputFile ++ abstract val mappings: RegularFileProperty ++ ++ @get:OutputFile ++ abstract val outputJar: RegularFileProperty ++ ++ override fun init() { ++ super.init() ++ outputJar.convention(defaultOutput()) ++ } ++ ++ @TaskAction ++ private fun addMappings() { ++ outputJar.get().asFile.parentFile.mkdirs() ++ inputJar.get().asFile.copyTo(outputJar.get().asFile, overwrite = true) ++ outputJar.get().path.openZip().use { fs -> ++ val dir = fs.getPath("META-INF/mappings/") ++ Files.createDirectories(dir) ++ val target = dir.resolve("reobf.tiny") ++ Files.copy(mappings.path, target) ++ } ++ } ++} ++ ++val includeMappings = tasks.register<IncludeMappings>("includeMappings") { ++ inputJar.set(tasks.fixJarForReobf.flatMap { it.outputJar }) ++ mappings.set(tasks.reobfJar.flatMap { it.mappingsFile }) ++} ++ ++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") + } +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)); + } + +diff --git a/src/main/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java b/src/main/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c701ef3c287f62aa0ebfbdbd6da6ed82a1c7793c +--- /dev/null ++++ b/src/main/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java +@@ -0,0 +1,38 @@ ++package io.papermc.paper.logging; ++ ++import io.papermc.paper.util.StacktraceDeobfuscator; ++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 StacktraceDeobfuscatingRewritePolicy() { ++ } ++ ++ @Override ++ public @NonNull LogEvent rewrite(final @NonNull LogEvent rewrite) { ++ final Throwable thrown = rewrite.getThrown(); ++ if (thrown != null) { ++ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thrown); ++ return new Log4jLogEvent.Builder(rewrite) ++ .setThrownProxy(null) ++ .build(); ++ } ++ return rewrite; ++ } ++ ++ @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..b8b17d046f836c8652ab094db00ab1af84971b2c +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/ObfHelper.java +@@ -0,0 +1,146 @@ ++package io.papermc.paper.util; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.InputStreamReader; ++import java.nio.charset.StandardCharsets; ++import java.util.HashMap; ++import java.util.HashSet; ++import java.util.Map; ++import java.util.Set; ++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; ++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; ++ ++ public static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn"; ++ public static final String SPIGOT_NAMESPACE = "spigot"; ++ ++ 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 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)) ++ ); ++ } ++ ++ final ClassMapping map = new ClassMapping( ++ cls.getName(SPIGOT_NAMESPACE).replace('/', '.'), ++ cls.getName(MOJANG_PLUS_YARN_NAMESPACE).replace('/', '.'), ++ Map.copyOf(methods) ++ ); ++ classes.add(map); ++ } ++ ++ return Set.copyOf(classes); ++ } catch (final IOException ex) { ++ System.err.println("Failed to load mappings for stacktrace deobfuscation."); ++ ex.printStackTrace(); ++ return null; ++ } ++ } ++ ++ public static String methodKey(final String obfName, final String obfDescriptor) { ++ return obfName + obfDescriptor; ++ } ++ ++ 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( ++ String obfName, ++ String mojangName, ++ Map<String, String> methodsByObf ++ ) {} ++} +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..eb910d4abf91488fa7cf1f5d47e0ee916c47f512 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java +@@ -0,0 +1,163 @@ ++package io.papermc.paper.util; ++ ++import io.papermc.paper.configuration.GlobalConfiguration; ++import it.unimi.dsi.fastutil.ints.IntArrayList; ++import it.unimi.dsi.fastutil.ints.IntList; ++import java.io.IOException; ++import java.io.InputStream; ++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<Class<?>, Map<String, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) { ++ @Override ++ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Map<String, IntList>> 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) { ++ final Map<String, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap); ++ for (final var entry : lineMap.entrySet()) { ++ final String methodKey = 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 methodKey; ++ } ++ } ++ } ++ 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<String, IntList> buildLineMap(final Class<?> key) { ++ final Map<String, IntList> lineMap = new HashMap<>(); ++ 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(); ++ lineMap.put(ObfHelper.methodKey(this.name, this.descriptor), this.lines); ++ } ++ } ++ 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 { ++ 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/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java +index 2d5494d2813b773e60ddba6790b750a9a08f21f8..7695bf44503f161523ea612ef8a884ae574a2e21 100644 +--- a/src/main/java/io/papermc/paper/util/TraceUtil.java ++++ b/src/main/java/io/papermc/paper/util/TraceUtil.java +@@ -6,13 +6,15 @@ public final class TraceUtil { + + 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(); + } + } +diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java +index 30a58229aa6dac5039511d0c0df5f2912ea7de9f..abe37c7c3c6f5ab73afd738ec78f06d7e4d2ed96 100644 +--- a/src/main/java/net/minecraft/CrashReport.java ++++ b/src/main/java/net/minecraft/CrashReport.java +@@ -32,6 +32,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 f114d5dab86aa2cdd59c78406c9d82f9caededca..99fa9f1952ee7ed79b223ff210a658e4b119b3e4 100644 +--- 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; + } + } +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index a283cce82069bf5dfd64229d102a19dfda158daf..b8e35f493458ba9e072953dffe6b2429f1d821ec 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -62,13 +62,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> { + }); + public static final AttributeKey<ConnectionProtocol> ATTRIBUTE_PROTOCOL = AttributeKey.valueOf("protocol"); + public static final LazyLoadedValue<NioEventLoopGroup> NETWORK_WORKER_GROUP = new LazyLoadedValue<>(() -> { +- 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 LazyLoadedValue<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = new LazyLoadedValue<>(() -> { +- 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 LazyLoadedValue<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = new LazyLoadedValue<>(() -> { +- 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; + private final Queue<Connection.PacketHolder> queue = Queues.newConcurrentLinkedQueue(); +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index abfcba1c3db090563191e8adea6d8250ae2d138e..487991163f12c1ded3f5d35a718aa89b1fb9278f 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -200,6 +200,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + 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); +diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +index a24ef433d0c9d06b86fd612978cfd6d877036791..1b38326c9a709536dc4cccf9af93aede98a1a782 100644 +--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java ++++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +@@ -54,10 +54,10 @@ public class ServerConnectionListener { + + private static final Logger LOGGER = LogUtils.getLogger(); + public static final LazyLoadedValue<NioEventLoopGroup> SERVER_EVENT_GROUP = new LazyLoadedValue<>(() -> { +- 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 LazyLoadedValue<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = new LazyLoadedValue<>(() -> { +- 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/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() { +diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java +index 383c52c62f49b17db2fbf58009d6ea132d124bea..e0a71bfc1498a517456b21747ab6ef3f1067a3f3 100644 +--- a/src/main/java/org/spigotmc/WatchdogThread.java ++++ b/src/main/java/org/spigotmc/WatchdogThread.java +@@ -105,7 +105,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa + 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 ); + } +@@ -193,7 +193,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa + } + 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 d285dbec16272db6b8a71865e05924ad66087407..1a05d23ff886b015fb9396f119822c678a47ec6f 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"> |