mirror of
https://github.com/YatopiaMC/Yatopia.git
synced 2024-12-03 08:05:02 +01:00
259 lines
13 KiB
Diff
259 lines
13 KiB
Diff
|
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
|