258 lines
14 KiB
Diff
258 lines
14 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 c525e93712ceea029ed50237932d998610249205..434d7c02001d16e542fd892ded23085fcf9e78a2 100644
|
|
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
|
|
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
|
|
@@ -618,7 +618,11 @@ public final class SimplePluginManager implements PluginManager {
|
|
// Paper start
|
|
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish
|
|
- 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
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish
|
|
callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerPluginEnableDisableException(msg, ex, plugin)));
|
|
}
|
|
@@ -692,7 +696,11 @@ public final class SimplePluginManager implements PluginManager {
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.setEventContext(event, registration); // Pufferfish
|
|
// 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
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.removeEventContext(); // Pufferfish
|
|
if (!(event instanceof com.destroystokyo.paper.event.server.ServerExceptionEvent)) { // We don't want to cause an endless event loop
|
|
callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
|
|
@@ -978,4 +986,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 3bea5dd67ad0393160ccede4ac99a3c7baa1803b..65f1aa94652d0efacb0184bf41b519adc26bef50 100644
|
|
--- a/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
|
|
+++ b/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
|
|
@@ -112,7 +112,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 a16c841751bc44e46d27fc4efbfc2ab23cc7b0d7..d1ba0d556edf6675952cb74dd31254704a9588cb 100644
|
|
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
|
|
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
|
|
@@ -370,7 +370,11 @@ public final class JavaPluginLoader implements PluginLoader {
|
|
jPlugin.setEnabled(true);
|
|
} catch (Throwable ex) {
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish
|
|
- 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
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish
|
|
// Paper start - Disable plugins that fail to load
|
|
this.server.getPluginManager().disablePlugin(jPlugin);
|
|
@@ -401,7 +405,11 @@ public final class JavaPluginLoader implements PluginLoader {
|
|
jPlugin.setEnabled(false);
|
|
} catch (Throwable ex) {
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.setPluginContext(plugin); // Pufferfish
|
|
- 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
|
|
gg.pufferfish.pufferfish.sentry.SentryContext.removePluginContext(); // Pufferfish
|
|
}
|
|
|
|
@@ -418,10 +426,18 @@ public final class JavaPluginLoader implements PluginLoader {
|
|
try {
|
|
loader.close();
|
|
} catch (IOException ex) {
|
|
- //
|
|
+ // Yatopia start - detailed report
|
|
this.server.getLogger().log(Level.WARNING, "Error closing the PluginClassLoader for '" + plugin.getDescription().getFullName() + "'", ex); // Paper - log exception
|
|
+ org.yatopiamc.yatopia.api.internal.StackTraceUtils.print(ex, _msg -> server.getLogger().log(Level.WARNING, _msg));
|
|
+ server.getLogger().log(Level.WARNING, org.yatopiamc.yatopia.api.internal.StackTraceUtils.EXCEPTION_DETAILS_BELOW, ex);
|
|
+ // Yatopia 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 4f736a267eb4c8a3bedb2d02fb30468484b991d5..c6102c9b40a05eb8ddaef40ce1c9bb8e14371389 100644
|
|
--- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
|
|
+++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java
|
|
@@ -251,4 +251,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 7cb7618df43793836a3151925b8c615628d707f9..6734be90d7121755ca3f81138ffaa8955152eee2 100644
|
|
--- a/src/test/java/org/bukkit/AnnotationTest.java
|
|
+++ b/src/test/java/org/bukkit/AnnotationTest.java
|
|
@@ -57,8 +57,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
|