Yatopia/patches/api/0008-Suspected-plugins-report.patch

259 lines
13 KiB
Diff
Raw Normal View History

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: ishland <ishlandmc@yeah.net>
Date: Fri, 29 Jan 2021 09:57:47 +0800
Subject: [PATCH] Suspected plugins report
Added "Suspected Plugins" to Watchdog, crash reports and exception messages
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 26685f59b235ea5b4c4fb7ae21acb5149edaa2b3..02c20a33161094b08dc2ae9353c0504561b0b452 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -560,7 +560,11 @@ public final class SimplePluginManager implements PluginManager {
// Paper start
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
- server.getLogger().log(Level.SEVERE, msg, ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, msg);
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin)));
}
// Paper end
@@ -621,7 +625,11 @@ public final class SimplePluginManager implements PluginManager {
} catch (Throwable ex) {
// Paper start - error reporting
String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
- server.getLogger().log(Level.SEVERE, msg, ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, msg);
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
}
@@ -905,4 +913,10 @@ public final class SimplePluginManager implements PluginManager {
}
// Paper end
+ // Yatopia start - Accessor
+ @NotNull
+ public Collection<PluginLoader> getPluginLoaders() {
+ return new HashSet<>(fileAssociations.values());
+ }
+ // Yatopia end
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
index 04fa3991f6ce4e9dad804f28fc6c947695857089..cb11eab6e13ed1c395b8f7db033c9a2817f4089c 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
@@ -111,7 +111,7 @@ public abstract class JavaPlugin extends PluginBase {
* @return File containing this plugin
*/
@NotNull
- protected File getFile() {
+ public File getFile() { // Yatopia
return file;
}
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index 384edf9890dfbd1cddfdcac4db1ebe9a4d761f78..7c0c63c3f5734d59aa8b57fe3eb3c1fe0e137f12 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -379,7 +379,11 @@ public final class JavaPluginLoader implements PluginLoader {
try {
jPlugin.setEnabled(true);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
// Paper start - Disable plugins that fail to load
server.getPluginManager().disablePlugin(jPlugin, true); // Paper - close Classloader on disable - She's dead jim
return;
@@ -414,7 +418,11 @@ public final class JavaPluginLoader implements PluginLoader {
try {
jPlugin.setEnabled(false);
} catch (Throwable ex) {
- server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
+ // Yatopia start - detailed report
+ server.getLogger().log(Level.SEVERE, "Error occurred while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)");
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.SEVERE, _msg));
+ server.getLogger().log(Level.SEVERE, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
+ // Yatopia end
}
if (cloader instanceof PluginClassLoader) {
@@ -432,11 +440,20 @@ public final class JavaPluginLoader implements PluginLoader {
loader.close();
}
} catch (IOException e) {
+ // Yatopia start - detailed report
server.getLogger().log(Level.WARNING, "Error closing the Plugin Class Loader for " + plugin.getDescription().getFullName());
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(e, _msg -> server.getLogger().log(Level.WARNING, _msg));
+ server.getLogger().log(Level.WARNING, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, e);
+ // Yatopia end
e.printStackTrace();
}
// Paper end
}
}
}
+ // Yatopia start - Accessor
+ public List<PluginClassLoader> getClassLoaders() {
+ return java.util.Collections.unmodifiableList(loaders);
+ }
+ // Yatopia end
}
diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
index 7760be3e34fa20825faf145d9fb5b2855c1a4602..2e8f3efdb683c44b3e42bb0187bc907e64cde288 100644
--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
@@ -232,4 +232,13 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot
'}';
}
// Paper end
+
+ // Yatopia start - Accessor
+ public java.util.Collection<Class<?>> getLoadedClasses() {
+ return java.util.Collections.unmodifiableCollection(
+ new java.util.HashSet<>(classes.values()).stream()
+ .filter(clazz -> clazz.getClassLoader() == this).collect(java.util.stream.Collectors.toSet())
+ );
+ }
+ // Yatopia end
}
diff --git a/src/main/java/org/yatopiamc/yatopia/api/internal/StackTraceUtils.java b/src/main/java/org/yatopiamc/yatopia/api/internal/StackTraceUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..0aa9bc6ad0a85d469b29201b9da29165bafb874c
--- /dev/null
+++ b/src/main/java/org/yatopiamc/yatopia/api/internal/StackTraceUtils.java
@@ -0,0 +1,105 @@
+package org.yatopiamc.yatopia.api.internal;
+
+import com.google.common.base.Suppliers;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginLoader;
+import org.bukkit.plugin.SimplePluginManager;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.plugin.java.JavaPluginLoader;
+import org.bukkit.plugin.java.PluginClassLoader;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class StackTraceUtils {
+
+ public static final String EXCEPTION_DETAILS_BELOW = "Exception details below: ";
+
+ private static final Supplier<Map<Plugin, Set<Class<?>>>> loadedClassesSupplier = Suppliers.memoizeWithExpiration(StackTraceUtils::scanForPluginClasses, 5, TimeUnit.SECONDS);
+
+ public static void print(StackTraceElement[] stackTrace, Consumer<String> out) {
+ Set<Plugin> suspectedPlugins = getSuspectedPluginsFromStackTrace(stackTrace);
+
+ printSuspectedPlugins(out, suspectedPlugins);
+ }
+
+ public static void print(Throwable t, Consumer<String> out) {
+ Set<Plugin> suspectedPlugins = getSuspectedPluginsFromStackTrace(getStackTracesFromThrowable(t).toArray(new StackTraceElement[0]));
+
+ printSuspectedPlugins(out, suspectedPlugins);
+ }
+
+ private static Set<StackTraceElement> getStackTracesFromThrowable(Throwable t) {
+ if(t == null) return Collections.emptySet();
+ Set<StackTraceElement> elements = new ObjectOpenHashSet<>();
+ elements.addAll(getStackTracesFromThrowable(t.getCause()));
+ elements.addAll(Arrays.stream(t.getSuppressed()).flatMap(throwable -> getStackTracesFromThrowable(throwable).stream()).collect(Collectors.toSet()));
+ elements.addAll(Arrays.asList(t.getStackTrace()));
+ return elements;
+ }
+
+ private static void printSuspectedPlugins(Consumer<String> out, Set<Plugin> suspectedPlugins) {
+ if (!suspectedPlugins.isEmpty()) {
+ out.accept("Suspected Plugins: ");
+ for (Plugin plugin : suspectedPlugins) {
+ StringBuilder builder = new StringBuilder("\t");
+ builder.append(plugin.getName())
+ .append("{")
+ .append(plugin.isEnabled() ? "enabled" : "disabled")
+ .append(",").append("ver=").append(plugin.getDescription().getVersion());
+ if (!plugin.isNaggable())
+ builder.append(",").append("nag");
+ if (plugin instanceof JavaPlugin)
+ builder.append(",").append("path=").append(((JavaPlugin) plugin).getFile());
+
+ builder.append("}");
+ out.accept(builder.toString());
+ }
+ } else {
+ out.accept("Suspected Plugins: None");
+ }
+ }
+
+ private static Set<Plugin> getSuspectedPluginsFromStackTrace(StackTraceElement[] stackTrace) {
+ Map<Plugin, Set<Class<?>>> loadedClasses = loadedClassesSupplier.get();
+ Set<Plugin> suspectedPlugins = new HashSet<>();
+ for (StackTraceElement stackTraceElement : stackTrace) {
+ for (Map.Entry<Plugin, Set<Class<?>>> pluginSetEntry : loadedClasses.entrySet()) {
+ if (pluginSetEntry.getValue().stream().anyMatch(clazz -> clazz.getName().equals(stackTraceElement.getClassName())))
+ suspectedPlugins.add(pluginSetEntry.getKey());
+ }
+ }
+ return suspectedPlugins;
+ }
+
+ private static Map<Plugin, Set<Class<?>>> scanForPluginClasses() {
+ Map<Plugin, Set<Class<?>>> loadedClasses = new Object2ObjectOpenHashMap<>();
+ if (Bukkit.getPluginManager() instanceof SimplePluginManager) {
+ final SimplePluginManager pluginManager = (SimplePluginManager) Bukkit.getPluginManager();
+ final Collection<PluginLoader> pluginLoaders = pluginManager.getPluginLoaders();
+ for (PluginLoader pluginLoader : pluginLoaders) {
+ if (pluginLoader instanceof JavaPluginLoader) {
+ JavaPluginLoader javaPluginLoader = (JavaPluginLoader) pluginLoader;
+ final List<PluginClassLoader> classLoaders = javaPluginLoader.getClassLoaders();
+ for (PluginClassLoader classLoader : classLoaders) {
+ loadedClasses.put(classLoader.getPlugin(), new ObjectOpenHashSet<>(classLoader.getLoadedClasses()));
+ }
+ }
+ }
+ }
+ return loadedClasses;
+ }
+
+}
diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
index 4c2780c903ec354edac741b673a7174284a9849a..ed5629295324123576af992cb1b4c7ce90ffa1de 100644
--- a/src/test/java/org/bukkit/AnnotationTest.java
+++ b/src/test/java/org/bukkit/AnnotationTest.java
@@ -49,8 +49,11 @@ public class AnnotationTest {
"co/aikar/timings/TimingHistory$2$1$2",
"co/aikar/timings/TimingHistory$3",
"co/aikar/timings/TimingHistory$4",
- "co/aikar/timings/TimingHistoryEntry$1"
+ "co/aikar/timings/TimingHistoryEntry$1",
// Paper end
+ // Yatopia start
+ "org/yatopiamc/yatopia/api/internal/StackTraceUtils"
+ // Yatopia end
};
@Test