Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-16 21:10:30 +01:00
Add /velocity heap command (#786)
Dieser Commit ist enthalten in:
Ursprung
d9ee34feac
Commit
d6c0b71648
@ -34,10 +34,13 @@ import com.velocitypowered.proxy.VelocityServer;
|
|||||||
import com.velocitypowered.proxy.util.InformationUtils;
|
import com.velocitypowered.proxy.util.InformationUtils;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -46,8 +49,8 @@ import java.util.Date;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import net.kyori.adventure.identity.Identity;
|
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.TextComponent;
|
import net.kyori.adventure.text.TextComponent;
|
||||||
import net.kyori.adventure.text.TranslatableComponent;
|
import net.kyori.adventure.text.TranslatableComponent;
|
||||||
@ -89,6 +92,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
.put("plugins", new Plugins(server))
|
.put("plugins", new Plugins(server))
|
||||||
.put("reload", new Reload(server))
|
.put("reload", new Reload(server))
|
||||||
.put("dump", new Dump(server))
|
.put("dump", new Dump(server))
|
||||||
|
.put("heap", new Heap())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +102,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
.map(Map.Entry::getKey)
|
.map(Map.Entry::getKey)
|
||||||
.collect(Collectors.joining("|"));
|
.collect(Collectors.joining("|"));
|
||||||
String commandText = "/velocity <" + availableCommands + ">";
|
String commandText = "/velocity <" + availableCommands + ">";
|
||||||
source.sendMessage(Identity.nil(), Component.text(commandText, NamedTextColor.RED));
|
source.sendMessage(Component.text(commandText, NamedTextColor.RED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -212,7 +216,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public void execute(CommandSource source, String @NonNull [] args) {
|
||||||
if (args.length != 0) {
|
if (args.length != 0) {
|
||||||
source.sendMessage(Identity.nil(), Component.text("/velocity version", NamedTextColor.RED));
|
source.sendMessage(Component.text("/velocity version", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,8 +231,8 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
.translatable("velocity.command.version-copyright",
|
.translatable("velocity.command.version-copyright",
|
||||||
Component.text(version.getVendor()),
|
Component.text(version.getVendor()),
|
||||||
Component.text(version.getName()));
|
Component.text(version.getName()));
|
||||||
source.sendMessage(Identity.nil(), velocity);
|
source.sendMessage(velocity);
|
||||||
source.sendMessage(Identity.nil(), copyright);
|
source.sendMessage(copyright);
|
||||||
|
|
||||||
if (version.getName().equals("Velocity")) {
|
if (version.getName().equals("Velocity")) {
|
||||||
TextComponent embellishment = Component.text()
|
TextComponent embellishment = Component.text()
|
||||||
@ -245,7 +249,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
"https://github.com/PaperMC/Velocity"))
|
"https://github.com/PaperMC/Velocity"))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
source.sendMessage(Identity.nil(), embellishment);
|
source.sendMessage(embellishment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +270,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
public void execute(CommandSource source, String @NonNull [] args) {
|
||||||
if (args.length != 0) {
|
if (args.length != 0) {
|
||||||
source.sendMessage(Identity.nil(), Component.text("/velocity plugins", NamedTextColor.RED));
|
source.sendMessage(Component.text("/velocity plugins", NamedTextColor.RED));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +296,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
.key("velocity.command.plugins-list")
|
.key("velocity.command.plugins-list")
|
||||||
.color(NamedTextColor.YELLOW)
|
.color(NamedTextColor.YELLOW)
|
||||||
.args(listBuilder.build());
|
.args(listBuilder.build());
|
||||||
source.sendMessage(Identity.nil(), output);
|
source.sendMessage(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextComponent componentForPlugin(PluginDescription description) {
|
private TextComponent componentForPlugin(PluginDescription description) {
|
||||||
@ -377,7 +381,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
dump.add("config", proxyConfig);
|
dump.add("config", proxyConfig);
|
||||||
dump.add("plugins", InformationUtils.collectPluginInfo(server));
|
dump.add("plugins", InformationUtils.collectPluginInfo(server));
|
||||||
|
|
||||||
Path dumpPath = Paths.get("velocity-dump-"
|
Path dumpPath = Path.of("velocity-dump-"
|
||||||
+ new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
|
+ new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date())
|
||||||
+ ".json");
|
+ ".json");
|
||||||
try (BufferedWriter bw = Files.newBufferedWriter(
|
try (BufferedWriter bw = Files.newBufferedWriter(
|
||||||
@ -404,4 +408,81 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
|
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heap SubCommand.
|
||||||
|
*/
|
||||||
|
public static class Heap implements SubCommand {
|
||||||
|
private static final Logger logger = LogManager.getLogger(Heap.class);
|
||||||
|
private MethodHandle heapGenerator;
|
||||||
|
private Consumer<CommandSource> heapConsumer;
|
||||||
|
private final Path dir = Path.of("./dumps");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandSource source, String @NonNull [] args) {
|
||||||
|
try {
|
||||||
|
if (Files.notExists(dir)) {
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single lookup of the heap dump generator method is performed on execution
|
||||||
|
// to avoid assigning variables unnecessarily in case the user never executes the command
|
||||||
|
if (heapGenerator == null || heapConsumer == null) {
|
||||||
|
javax.management.MBeanServer server = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||||
|
MethodType type;
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName("openj9.lang.management.OpenJ9DiagnosticsMXBean");
|
||||||
|
type = MethodType.methodType(String.class, String.class, String.class);
|
||||||
|
|
||||||
|
this.heapGenerator = lookup.findVirtual(clazz, "triggerDumpToFile", type);
|
||||||
|
this.heapConsumer = (src) -> {
|
||||||
|
String name = "heap-dump-" + format.format(new Date());
|
||||||
|
Path file = dir.resolve(name + ".phd");
|
||||||
|
try {
|
||||||
|
Object openj9Mbean = ManagementFactory.newPlatformMXBeanProxy(
|
||||||
|
server, "openj9.lang.management:type=OpenJ9Diagnostics", clazz);
|
||||||
|
heapGenerator.invoke(openj9Mbean, "heap", file.toString());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// This should not occur
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
src.sendMessage(Component.text("Heap dump saved to " + file, NamedTextColor.GREEN));
|
||||||
|
};
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
Class<?> clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
|
||||||
|
type = MethodType.methodType(void.class, String.class, boolean.class);
|
||||||
|
|
||||||
|
this.heapGenerator = lookup.findVirtual(clazz, "dumpHeap", type);
|
||||||
|
this.heapConsumer = (src) -> {
|
||||||
|
String name = "heap-dump-" + format.format(new Date());
|
||||||
|
Path file = dir.resolve(name + ".hprof");
|
||||||
|
try {
|
||||||
|
Object hotspotMbean = ManagementFactory.newPlatformMXBeanProxy(
|
||||||
|
server, "com.sun.management:type=HotSpotDiagnostic", clazz);
|
||||||
|
this.heapGenerator.invoke(hotspotMbean, file.toString(), true);
|
||||||
|
} catch (Throwable e1) {
|
||||||
|
// This should not occur
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
src.sendMessage(Component.text("Heap dump saved to " + file, NamedTextColor.GREEN));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.heapConsumer.accept(source);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
source.sendMessage(Component.text("Failed to write heap dump, see server log for details",
|
||||||
|
NamedTextColor.RED));
|
||||||
|
logger.error("Could not write heap", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
||||||
|
return source.getPermissionValue("velocity.command.heap") == Tristate.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren