diff --git a/build.gradle b/build.gradle index 1469fcc..ca92fab 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,9 @@ application { } jar { + from('agent/build/libs/') { + include '**.jar' + } manifest { attributes( "Main-Class": application.mainClass.get(), diff --git a/src/main/java/jdump/cli/CommandLineArguments.java b/src/main/java/jdump/cli/CommandLineArguments.java index 47c4c87..307cc70 100644 --- a/src/main/java/jdump/cli/CommandLineArguments.java +++ b/src/main/java/jdump/cli/CommandLineArguments.java @@ -36,6 +36,7 @@ private void configure() { if (argList.contains("-T")) configuration.wantThreadDumpForAll(); if (argList.contains("-J")) configuration.wantJfrForAll(); if (argList.contains("-N")) configuration.wantNmtForAll(); + if (argList.contains("-M")) configuration.wantMallInfoForAll(); } setDuration(argList); setOutputDirectory(argList); @@ -57,15 +58,16 @@ private static Optional getLastOf(List argList, String parameter } private void showUsageInformation() { - System.out.println("Usage: jdump [-f] [-A] [-H] [-J] [-d] [-N] [-T]"); + System.out.println("Usage: jdump [-f] [-A] [-H] [-J] [-d] [-N] [-M] [-T]"); System.out.println(); System.out.println("Options:"); System.out.println("start without options to show UI, if not on a headless system"); System.out.println("-f: name of the target folder (will be created if non-existent; current working directoy, if empty)"); - System.out.println("-A: produce all types of dumps for all JVMs running locally"); + System.out.println("-A: produce all non-agent-based types of dumps for all JVMs running locally"); System.out.println("-H: produce heap dumps for all JVMs running locally"); System.out.println("-J: produce JFRs for all JVMs running locally"); System.out.println("-d: the duration selected for the JFRs, in seconds, default: 5"); + System.out.println("-M: produce mallinfo() stats on Linux with glibc"); System.out.println("-N: product Native Memory Tracks for all JVMs running locally"); System.out.println("-T: produce thread dumps for all JVMs running locally"); } diff --git a/src/main/java/jdump/dump/Attach.java b/src/main/java/jdump/dump/Attach.java index 1bb255d..dda8b57 100644 --- a/src/main/java/jdump/dump/Attach.java +++ b/src/main/java/jdump/dump/Attach.java @@ -1,19 +1,20 @@ package jdump.dump; -import com.sun.tools.attach.AttachNotSupportedException; -import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; +import com.sun.tools.attach.*; import sun.tools.attach.HotSpotVirtualMachine; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Helper for attaching to JVMs using the Attach API. */ class Attach { - static final Map attachedVMs = new HashMap<>(); /** @@ -47,4 +48,14 @@ static synchronized void detachAll() { attachedVMs.clear(); } + public synchronized static void loadAgent(VirtualMachineDescriptor vmd, String outputFileName) throws + AgentLoadException, IOException, AgentInitializationException { + if (!attachedVMs.containsKey(vmd.id())) throw new RuntimeException("JVM not attached"); + // TODO: Remove hard-coded references to agent versions + try (var stream = Attach.class.getResourceAsStream("/agent-1.0-SNAPSHOT.jar")) { + File tempFile = File.createTempFile("agent-1.0-SNAPSHOT-", ".jar"); + Files.copy(Objects.requireNonNull(stream), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + attachedVMs.get(vmd.id()).loadAgent(tempFile.getAbsolutePath(), outputFileName); + } + } } diff --git a/src/main/java/jdump/dump/Configuration.java b/src/main/java/jdump/dump/Configuration.java index ec20d49..63dd14b 100644 --- a/src/main/java/jdump/dump/Configuration.java +++ b/src/main/java/jdump/dump/Configuration.java @@ -19,6 +19,7 @@ public class Configuration { private String outputDirectory = System.getProperty("user.dir"); private Output.TYPE outputType = Output.TYPE.DIRECTORY; private boolean immutable; + private boolean wantMallInfoForAll = false; private Configuration() { immutable = true; @@ -32,6 +33,7 @@ private Configuration(Configuration configuration) { this.wantNmtForAll = configuration.wantNmtForAll; this.outputDirectory = configuration.outputDirectory; this.outputType = Output.TYPE.DIRECTORY; + this.wantMallInfoForAll = configuration.wantMallInfoForAll; immutable = true; } @@ -83,6 +85,14 @@ public Mutable outputType(Output.TYPE outputType) { super.outputType(outputType); return this; } + + public void wantMallInfoForAll() { + super.wantMallInfoForAll(); + } + } + + private void wantMallInfoForAll() { + this.wantMallInfoForAll = true; } private void outputType(Output.TYPE outputType) { @@ -129,6 +139,10 @@ public boolean nmtForAllSet() { return wantNmtForAll; } + public boolean mallInfoForAllSet() { + return wantMallInfoForAll; + } + public String outputDirectory() { return outputDirectory; } diff --git a/src/main/java/jdump/dump/Dumps.java b/src/main/java/jdump/dump/Dumps.java index 5321680..be057d1 100644 --- a/src/main/java/jdump/dump/Dumps.java +++ b/src/main/java/jdump/dump/Dumps.java @@ -15,6 +15,7 @@ public static void handle(Configuration configuration) { if (configuration.threadDumpForAllSet()) new ThreadDump(configuration).performForAll(); if (configuration.jfrForAllSet()) new JFRDump(configuration).performForAll(); if (configuration.nmtForAllSet()) new NMTDump(configuration).performForAll(); + if (configuration.mallInfoForAllSet()) new MallInfoDump(configuration).performForAll(); } /** diff --git a/src/main/java/jdump/dump/MallInfoDump.java b/src/main/java/jdump/dump/MallInfoDump.java new file mode 100644 index 0000000..be73cda --- /dev/null +++ b/src/main/java/jdump/dump/MallInfoDump.java @@ -0,0 +1,39 @@ +package jdump.dump; + +import com.sun.tools.attach.AgentInitializationException; +import com.sun.tools.attach.AgentLoadException; +import com.sun.tools.attach.AttachNotSupportedException; +import com.sun.tools.attach.VirtualMachineDescriptor; + +import java.io.File; +import java.io.IOException; + +public class MallInfoDump extends HotspotDump { + final Configuration configuration; + + + public MallInfoDump(Configuration configuration) { + this.configuration = configuration; + } + + + @Override + void performFor(VirtualMachineDescriptor vmd) { + try { + Attach.to(vmd); + } catch (IOException | AttachNotSupportedException e) { + throw new RuntimeException("Could not attach to JVM " + vmd.id()); + } + try { + System.out.println("Dumping mallinfo() statistics for JVM " + vmd.id()); + Attach.loadAgent(vmd, configuration.outputDirectory() + File.separator + filenameFor(vmd)); + } catch (AgentLoadException | IOException | AgentInitializationException e) { + throw new RuntimeException("Failed to load agent: " + e); + } + } + + @Override + String filenameFor(VirtualMachineDescriptor vmd) { + return "jdump-mallinfo-" + vmd.id() + ".txt"; + } +}