Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
Added functionality to export JVM information
Browse files Browse the repository at this point in the history
Closes #4.
All JVM related information provided by JVMUtility can
now be exported to a JSON file.
  • Loading branch information
the-c0d3br34k3r authored and silvs110 committed Feb 10, 2021
1 parent 444b4e1 commit 6f498bf
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 53 deletions.
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.padaiyal.utilities</groupId>
<artifactId>vaidhiyar</artifactId>
<version>2021.02.01</version>
<version>2021.02.09</version>

<parent>
<groupId>org.padaiyal</groupId>
Expand All @@ -27,6 +27,7 @@
<dependency.jProperties.version>2021.01.13</dependency.jProperties.version>
<dependency.jI18n.version>2021.01.14</dependency.jI18n.version>
<dependency.mockito.version>3.7.7</dependency.mockito.version>
<dependency.gson.version>2.8.5</dependency.gson.version>
</properties>

<dependencies>
Expand Down Expand Up @@ -58,5 +59,10 @@
<version>${dependency.mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${dependency.gson.version}</version>
</dependency>
</dependencies>
</project>
177 changes: 135 additions & 42 deletions src/main/java/org/padaiyal/utilities/vaidhiyar/JvmUtility.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package org.padaiyal.utilities.vaidhiyar;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.sun.management.HotSpotDiagnosticMXBean;
import com.sun.management.ThreadMXBean;
import java.io.BufferedWriter;
import java.io.IOException;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
Expand Down Expand Up @@ -44,10 +49,6 @@ public final class JvmUtility {
*/
private static final List<GarbageCollectorMXBean> garbageCollectorMxBeans
= ManagementFactory.getGarbageCollectorMXBeans();
/**
* HotSpotDiagnosticMXBean object used to retrieve heap dump and VM options.
*/
private static HotSpotDiagnosticMXBean hotSpotDiagnosticMXBean;
/**
* MemoryMXBean object used to get heap memory information.
*/
Expand All @@ -61,7 +62,6 @@ public final class JvmUtility {
* RuntimeMxBean object used to get CPU core count.
*/
private static final RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();

/**
* Map to store the thread CPU usages.
*/
Expand All @@ -71,6 +71,18 @@ public final class JvmUtility {
* ExecutorService object used to manage the thread CPU usage collector.
*/
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
/**
* HotSpotDiagnosticMXBean object used to retrieve heap dump and VM options.
*/
private static HotSpotDiagnosticMXBean hotSpotDiagnosticMXBean;
/**
* Switch to enable/disable thread CPU usage collection.
*/
private static boolean runThreadCpuUsageCollectorSwitch;
/**
* Sampling interval to use to measure the thread CPU usages.
*/
private static long cpuSamplingIntervalInMilliSeconds;
/**
* Callable implementation to execute when the thread CPU usage is to be collected.
*/
Expand All @@ -82,7 +94,6 @@ public final class JvmUtility {
}
return null;
};

/**
* Initialize dependant values.
*/
Expand All @@ -108,24 +119,17 @@ public final class JvmUtility {
"JvmUtility.cpuSamplingInterval.milliseconds"
);

hotSpotDiagnosticMXBean = ManagementFactory.newPlatformMXBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class
);
if (hotSpotDiagnosticMXBean == null) {
hotSpotDiagnosticMXBean = ManagementFactory.newPlatformMXBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class
);
}
} catch (IOException e) {
logger.error(e);
}
};

/**
* Switch to enable/disable thread CPU usage collection.
*/
private static boolean runThreadCpuUsageCollectorSwitch;
/**
* Sampling interval to use to measure the thread CPU usages.
*/
private static long cpuSamplingIntervalInMilliSeconds;
/**
* Stores the Future object returned when the thread CPU usage collector is triggered.
*/
Expand Down Expand Up @@ -164,7 +168,7 @@ public static ExtendedMemoryUsage getNonHeapMemoryUsage() {
/**
* Runs the garbage collector.
*
* @return Returns the duration taken for garbage collection.
* @return The duration taken for garbage collection.
*/
public static Duration runGarbageCollector() {
memoryMXBean.setVerbose(
Expand All @@ -189,12 +193,12 @@ public static Duration runGarbageCollector() {
}

/**
* Returns the following garbage collection info:
* Gets the following garbage collection info:
* - Collection count.
* - Collection time.
* - Memory pools in which garbage has been collected.
*
* @return Returns the garbage collection info.
* @return The garbage collection info.
*/
public static GarbageCollectionInfo[] getGarbageCollectionInfo() {
return garbageCollectorMxBeans.stream()
Expand Down Expand Up @@ -254,9 +258,7 @@ public static Duration generateHeapDump(
I18nUtility.getString("JvmUtility.generateHeapDump.generating")
);
Instant heapDumpGenerationStartInstant = Instant.now();
if (hotSpotDiagnosticMXBean == null) {
dependantValuesInitializer.run();
}
dependantValuesInitializer.run();
hotSpotDiagnosticMXBean.dumpHeap(
destinationHeapDumpFilePath.toAbsolutePath()
.toString(),
Expand All @@ -275,7 +277,27 @@ public static Duration generateHeapDump(
}

/**
* Returns the thread CPU usage collector future.
* Gets a list of available VM options.
*
* @return A list of available VM options.
*/
public static JsonArray getAllVmOptions() {
dependantValuesInitializer.run();
JsonArray vmOptions = new JsonArray();
hotSpotDiagnosticMXBean.getDiagnosticOptions()
.forEach(vmOption -> {
JsonObject vmOptionJsonObject = new JsonObject();
vmOptionJsonObject.addProperty("name", vmOption.getName());
vmOptionJsonObject.addProperty("value", vmOption.getValue());
vmOptionJsonObject.addProperty("origin", vmOption.getOrigin().toString());
vmOptionJsonObject.addProperty("isWriteable", vmOption.isWriteable());
vmOptions.add(vmOptionJsonObject);
});
return vmOptions;
}

/**
* Gets the thread CPU usage collector future.
*
* @return The thread CPU usage collector future.
*/
Expand All @@ -293,12 +315,11 @@ public static boolean isThreadCpuUsageCollectorRunning() {
}

/**
* Returns the switch used to toggle the state of the thread CPU usage collector.
* Gets the switch used to toggle the state of the thread CPU usage collector.
*
* @return State of the thread CPU usage collector.
* If true, it means that the thread CPU usage collector will keep running,
* else it means that the thread CPU usage collector has terminated or will terminate
* soon.
* @return State of the thread CPU usage collector. If true, it means that the thread CPU usage
* collector will keep running, else it means that the thread CPU usage collector has
* terminated or will terminate soon.
*/
public static boolean getRunThreadCpuUsageCollectorSwitch() {
return runThreadCpuUsageCollectorSwitch;
Expand Down Expand Up @@ -330,16 +351,14 @@ public static long[] getAllThreadsId() {
/**
* Gets the extended thread info for all JVM threads.
*
* @return Extended thread info for all JVM threads.
* @param threadStackDepth Depth of thread stack to retrieve.
* @return Extended thread info for all JVM threads.
*/
public static ExtendedThreadInfo[] getAllExtendedThreadInfo() {
public static ExtendedThreadInfo[] getAllExtendedThreadInfo(int threadStackDepth) {
return Arrays.stream(
threadMxBean.getThreadInfo(
threadMxBean.getAllThreadIds(),
PropertyUtility.getTypedProperty(
Integer.class,
"JvmUtility.thread.stackDepth"
)
threadStackDepth
)
).map(threadInfo -> new ExtendedThreadInfo(
threadInfo,
Expand Down Expand Up @@ -400,8 +419,8 @@ public static double getCpuUsage(long threadId) {
* Computes the thread CPU usages given the necessary inputs.
*
* @param initialThreadCpuTimesInNanoSeconds Initial thread CPU times in nano seconds.
* @param currentThreadCpuTimesInNanoSeconds Thread CPU times at the end of the sampling
* duration in nano seconds.
* @param currentThreadCpuTimesInNanoSeconds Thread CPU times at the end of the sampling duration
* in nano seconds.
* @param samplingDurationInMilliSeconds Sampling duration in milli seconds.
* @return A map, mapping the thread ID to it's CPU usage.
*/
Expand Down Expand Up @@ -455,8 +474,8 @@ static Map<Long, Double> getThreadCpuUsages(
}

double cpuThreadTimeInMilliSeconds = (
currentThreadCpuTimesInNanoSeconds.get(threadId)
- initialThreadCpuTimesInNanoSeconds.get(threadId)
currentThreadCpuTimesInNanoSeconds.get(threadId)
- initialThreadCpuTimesInNanoSeconds.get(threadId)
) / (1000.0 * 1000.0);
double threadCpuUsage
= cpuThreadTimeInMilliSeconds * 100.0 / samplingDurationInMilliSeconds;
Expand Down Expand Up @@ -515,4 +534,78 @@ static void runCpuUsageCollector() throws InterruptedException {
);
}

/**
* Dump the JVM information onto a JSON file in the specified directory.
*
* @param destinationDirectory Directory in which to create the JSON file.
* @param fileName Name of the JSON file to create.
* @param threadStackDepth Depth of the thread stack to obtain.
* @throws IOException If there is an issue writing the JSON file.
*/
public static void dumpJvmInformationToFile(
Path destinationDirectory,
String fileName,
int threadStackDepth
) throws IOException {
// Input validation
Objects.requireNonNull(destinationDirectory);
if (!Files.exists(destinationDirectory)) {
throw new IllegalArgumentException(
I18nUtility.getFormattedString(
"JvmUtility.error.destinationPathDoesNotExist",
destinationDirectory
)
);
} else if (!Files.isDirectory(destinationDirectory)) {
throw new IllegalArgumentException(
I18nUtility.getFormattedString(
"JvmUtility.error.destinationPathNotADirectory",
destinationDirectory
)
);
}
Objects.requireNonNull(fileName);

@SuppressWarnings("SpellCheckingInspection")
Gson gsonObject = new GsonBuilder()
.setPrettyPrinting()
.create();
JsonObject jvmInformation = new JsonObject();
jvmInformation.addProperty(
"timestamp",
Instant.now().toString()
);
jvmInformation.add(
"heapUsage",
getHeapMemoryUsage().toJsonObject()
);
jvmInformation.add(
"nonHeapUsage",
getNonHeapMemoryUsage().toJsonObject()
);
jvmInformation.add(
"vmOptions",
getAllVmOptions()
);
jvmInformation.add(
"garbageCollectionInfos",
gsonObject.toJsonTree(getGarbageCollectionInfo())
);
JsonArray extendedThreadInfos = new JsonArray();
Arrays.stream(getAllExtendedThreadInfo(threadStackDepth))
.map(ExtendedThreadInfo::toJsonObject)
.forEach(extendedThreadInfos::add);
jvmInformation.add("threadInfos", extendedThreadInfos);

try (
BufferedWriter bufferedWriter = Files.newBufferedWriter(
destinationDirectory.resolve(fileName + ".json")
)
) {
gsonObject.toJson(
jvmInformation,
bufferedWriter
);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.padaiyal.utilities.vaidhiyar.abstractions;

import com.google.gson.JsonObject;
import java.lang.management.MemoryUsage;

/**
* An extension on the MemoryUsage object.
* https://docs.oracle.com/en/java/javase/14/docs/api/java.management/java/lang/management/
* An extension on the MemoryUsage object. https://docs.oracle.com/en/java/javase/14/docs/api/java.management/java/lang/management/
* GarbageCollectorMXBean.html
*/
public class ExtendedMemoryUsage extends MemoryUsage {
Expand Down Expand Up @@ -76,4 +76,22 @@ public ExtendedMemoryUsage(MemoryUsage memoryUsage) {
public double getMemoryUsagePercentage() {
return memoryUsagePercentage;
}

/**
* Gets a JSON representation of an instance of this class.
*
* @return JSON representation of an instance of this class.
*/
public JsonObject toJsonObject() {
JsonObject extendedMemoryUsageJsonObject = new JsonObject();
extendedMemoryUsageJsonObject.addProperty("initMemoryInBytes", getInit());
extendedMemoryUsageJsonObject.addProperty("usedMemoryInBytes", getUsed());
extendedMemoryUsageJsonObject.addProperty("committedMemoryInBytes", getCommitted());
extendedMemoryUsageJsonObject.addProperty("maxMemoryInBytes", getMax());
extendedMemoryUsageJsonObject.addProperty(
"memoryUsagePercentage",
getMemoryUsagePercentage()
);
return extendedMemoryUsageJsonObject;
}
}
Loading

0 comments on commit 6f498bf

Please sign in to comment.