diff --git a/.gitignore b/.gitignore index 59f307b..6980d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties .mvn/wrapper/maven-wrapper.jar - +*.csv # End of https://www.gitignore.io/api/maven diff --git a/README.md b/README.md index 58d1ab9..10f2937 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Description - Resource Monitor for Java is a tool that helps to monitor Java process resources and detect memory leaks/CPU constrains. + Resource Monitor for Java is a tool that **helps** to **monitor Java process** resources and detect memory **leaks**/CPU constrains. ## How to use? @@ -11,11 +11,13 @@ ``` rmjvm 1.0 -usage: rmjvm [-c] [-e ] [-ed ] [-h] [-ho ] [-p ] [-s - ] +usage: rmjvm [-c] [-cf ] [-e ] [-ed ] [-h] [-ho ] [-p + ] [-s ] -c,--check check will run all the actionsand wait until it is requested to stop. Meanwhile it will monitoring the memory and compare + -cf,--config the configuration file (by default + conf/rmjvm.yml) -e,--export export format (csv, output) -ed,--exportdirectory export directory where will be stored the files. @@ -25,7 +27,6 @@ usage: rmjvm [-c] [-e ] [-ed ] [-h] [-ho ] [-p ] [-s process -p,--port set the port for JMX of java listen process -s,--skip skip the cpu or memory (--skip=mem,cpu) - ``` ## Examples @@ -37,22 +38,76 @@ Few examples of use of the application: $ rmjvm --help $ rmjvm --version $ rmjvm --check -$ rmjvm --skip=mem -$ rmjvm --skip=cpu,mem -$ rmjvm --export=csv --exportdirectory=/tmp/dump-reports +$ rmjvm --skip mem +$ rmjvm --skip cpu,mem +$ rmjvm --export csv --exportdirectory /tmp/dump-reports --config conf/rmjvm-myApp.yml + +``` + + +## Configurations + +``` +### Please modify according your needs +application: + uri: "service:jmx:rmi:///jndi/rmi://:/jmxrmi" + directory: "" + + +# This is just few examples for monitoring + +actions: + - name: monitor01 + cmd: "sendFilesApp -h hostname" + timeout: 200 # ms + monitor: ['cpu', 'mem'] + executions: 20 + - name: monitor02 + cmd: "receiveFilesApp -h hostname" + monitor: ['mem'] + timeout: 100 # ms + executions: 2 + +# configure a tracer +# there are few examples, such zipkin +tracer: + - uri: "http://my-zipkin-deployment:9201" + +``` + + +## Configure your Java Application +You will need to configure the following properties, that will allow to JMX connection allowed by +remote applications: + +``` + -Dcom.sun.management.jmxremote.port=3333 + -Dcom.sun.management.jmxremote.ssl=false + -Dcom.sun.management.jmxremote.authenticate=false ``` +## Output - export format + + + + + + ## Recommended tools - You should be aware at least of two tools: + You should be aware at least of the following tools, that probably could help you find your issues: - jConsole - Mission Control and Flight Recorder - Oracle VisualVM - JVM tools: https://github.com/aragozin/jvm-tools/ - Eclipse Memory Analyser: MAT + + **Note**: rmjvm was mainly developed to track the memory after specific commands and to know exactly what is increasing over the + time. Other tools are allowed to do real time monitoring and explore/monitoring and trigger specific JMX Mbeans. The main + reason to build rmjvm was mainly due to some programatic triggers that was needed. ## Other resources diff --git a/bin/rmjvm.sh b/bin/rmjvm.sh index 20d602b..865c95f 100644 --- a/bin/rmjvm.sh +++ b/bin/rmjvm.sh @@ -1,2 +1,3 @@ #!/usr/bin/env bash +java -jar rmjvm-jar-with-dependencies.jar "$@" \ No newline at end of file diff --git a/conf/rmjvm-dicoogle.yml b/conf/rmjvm-dicoogle.yml index edb3f6e..dd9ce23 100644 --- a/conf/rmjvm-dicoogle.yml +++ b/conf/rmjvm-dicoogle.yml @@ -15,12 +15,12 @@ actions: cmd: "c:\\Users\\bastiao\\Software\\dcm4che-5.16.3-bin\\dcm4che-5.16.3\\bin\\ianscu.bat -c DICOOGLE-STORAGE@localhost:6666" timeout: 0 # ms monitor: ['cpu', 'mem'] - executions: 20 + executions: 10 - name: monitor-dicom-qr cmd: "c:\\Users\\bastiao\\Software\\dcm4che-5.16.3-bin\\dcm4che-5.16.3\\bin\\ianscu.bat -c DICOOGLE-STORAGE@localhost:1045" monitor: ['mem'] timeout: 0 # ms - executions: 2 + executions: 100 # configure a tracer # there are few examples, such zipkin diff --git a/pom.xml b/pom.xml index 389397a..951a4d2 100644 --- a/pom.xml +++ b/pom.xml @@ -27,26 +27,56 @@ - maven-assembly-plugin - - - - com.bmdsoftware.monitor.resourcemonitor.Main - - - - jar-with-dependencies - - - - - make-assembly - package - - single - - - + maven-assembly-plugin + + + + com.bmdsoftware.monitor.resourcemonitor.Main + + + + + rmjvm + + jar-with-dependencies + + + + + + make-assembly + package + + single + + + + + + + maven-resources-plugin + 3.1.0 + + + copy-resources + + validate + + copy-resources + + + ${basedir}/ + + + bin/ + true + + + + + @@ -89,32 +119,38 @@ commons-cli 1.4 - + org.yaml snakeyaml 1.21 - - io.zipkin.brave - brave - 5.6.3 - - - - - io.zipkin.reporter2 - zipkin-reporter - 2.6.0 - - - io.zipkin.reporter2 - zipkin-sender-okhttp3 - 2.6.0 - - - + + io.zipkin.brave + brave + 5.6.3 + + + + org.apache.commons + commons-csv + 1.4 + + + + + io.zipkin.reporter2 + zipkin-reporter + 2.6.0 + + + io.zipkin.reporter2 + zipkin-sender-okhttp3 + 2.6.0 + + + org.junit.jupiter junit-jupiter-api diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/Main.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/Main.java index 1492bd6..b7114c7 100644 --- a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/Main.java +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/Main.java @@ -1,18 +1,18 @@ /** * Copyright (c) 2019, BMD software * All rights reserved. - * + *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -31,6 +31,7 @@ import java.io.IOException; import java.lang.management.MonitorInfo; import java.net.MalformedURLException; +import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; @@ -55,7 +56,12 @@ import javax.management.remote.JMXServiceURL; import com.bmdsoftware.monitor.resourcemonitor.actions.Action; +import com.bmdsoftware.monitor.resourcemonitor.actions.imp.ProcessRun; import com.bmdsoftware.monitor.resourcemonitor.conf.YmlConfig; +import com.bmdsoftware.monitor.resourcemonitor.cpu.CPUMonitor; +import com.bmdsoftware.monitor.resourcemonitor.cpu.imp.CPUMonitorCheck; +import com.bmdsoftware.monitor.resourcemonitor.export.EntrySummary; +import com.bmdsoftware.monitor.resourcemonitor.export.ExporterCSV; import com.bmdsoftware.monitor.resourcemonitor.jmx.JMXConnection; import com.bmdsoftware.monitor.resourcemonitor.memory.MemoryChecker; import com.bmdsoftware.monitor.resourcemonitor.memory.imp.MemoryCheckImp; @@ -74,14 +80,14 @@ /** * * The main class for the rmjvm - * + * * @author Luís A. Bastião Silva */ public class Main { private static String PROJECT_NAME = "rmjvm"; private static String VERSION = "1.0"; - + private static Logger logger = LoggerFactory.getLogger(Main.class); private boolean check = false; @@ -95,11 +101,16 @@ public class Main { // Yml Configuration. private YmlConfig config = null; + private MemoryChecker memoryChecker; + private CPUMonitor cpuMonitorChecker; + + private ExporterCSV exporterCSV; + /** - * + * * Constructor with major settings - * + * * @param check * @param checkMemory * @param checkCPU @@ -124,7 +135,6 @@ public Main() { } - public static void main(String[] args) { logger.info("ResourceMonitor: started"); @@ -138,7 +148,7 @@ public static void main(String[] args) { Option help = new Option("h", "help", false, "help shows how to use the rmjvm and what its core funcionality"); options.addOption(help); - Option hostOpt = new Option("ho", "host", false, "set the hostname for JMX of java listen process"); + Option hostOpt = new Option("ho", "host", false, "set the hostname for JMX of java listen process"); hostOpt.setArgs(1); options.addOption(hostOpt); @@ -146,9 +156,9 @@ public static void main(String[] args) { portOpt.setArgs(1); options.addOption(portOpt); - + Option checkOpt = new Option("c", "check", false, "check will run all the actions" + - "and wait until it is requested to stop. Meanwhile it will monitoring the memory and compare "); + "and wait until it is requested to stop. Meanwhile it will monitoring the memory and compare "); checkOpt.setArgs(0); options.addOption(checkOpt); @@ -157,20 +167,40 @@ public static void main(String[] args) { options.addOption(skipOpt); - Option exportOpt = new Option("e", "export", false, "export format (csv, output) "); + Option exportOpt = new Option("e", "export", true, + "export format (csv, output) "); exportOpt.setArgs(1); options.addOption(exportOpt); Option exportDirOpt = new Option("ed", "exportdirectory", false, "export directory where will be stored the files."); exportDirOpt.setArgs(1); options.addOption(exportDirOpt); - + + + Option configOpt = new Option("cf", "config", false, "the configuration file (by default conf/rmjvm.yml)"); + configOpt.setArgs(1); + options.addOption(configOpt); // Configure Command Line Parser CommandLineParser parser = new DefaultParser(); CommandLine cmd = null; Main app = new Main(); - + + + // Parse options from command line. + try { + cmd = parser.parse(options, args); + } catch (ParseException e) { + logger.error("ResourceMonitor: error while parsing the command line options", e); + System.err.println("Error: error while parsing the command line options" + configurationFile); + System.exit(Consts.ERROR_WHILE_PARSING); + } + + if (cmd.hasOption("config")) { + String configFile = cmd.getOptionValue("config"); + configurationFile = configFile; + } + // Load the configuration file. // If any options is passed by the command line, the command line will overwrite // the configurations file @@ -185,83 +215,103 @@ public static void main(String[] args) { System.exit(Consts.ERROR_WHILE_PARSING); } - // Parse options from command line. - try { - cmd = parser.parse( options, args); - } catch (ParseException e) { - logger.error("ResourceMonitor: error while parsing the command line options", e); - System.err.println("Error: error while parsing the command line options" + configurationFile); - System.exit(Consts.ERROR_WHILE_PARSING); - } // Check the options and call the correct options. - if (cmd.hasOption("help")) { + if (cmd.hasOption("help") || args.length ==0) { HelpFormatter formatter = new HelpFormatter(); System.out.println(PROJECT_NAME + " " + VERSION); formatter.printHelp(PROJECT_NAME, options, true); + System.exit(0); + } else if (cmd.hasOption("version")) { System.out.println("version: " + VERSION); - } - else if (cmd.hasOption("check")) { + } else if (cmd.hasOption("check")) { System.out.println("Checking .."); app.setCheck(true); - } - else if (cmd.hasOption("host")) { + } else if (cmd.hasOption("host")) { System.out.println("Host: "); String hostname = cmd.getOptionValue("host"); app.setHostname(hostname); - } - else if (cmd.hasOption("port")) { + } else if (cmd.hasOption("port")) { System.out.println("Port: "); String port = cmd.getOptionValue("port"); Integer portInt = Integer.parseInt(port); app.setPort(portInt); - } - else if (cmd.hasOption("skip")) { + } else if (cmd.hasOption("skip")) { // Get what are going skip analysis. - String [] skipOpts = cmd.getOptionValues("skip"); - for (int i = 0; i actions = this.config.getActions(); + JMXConnection connection = new JMXConnection(config.getUriJmxService()); + memoryChecker = new MemoryCheckImp(connection, config.getApplicationDirectory()); + cpuMonitorChecker = new CPUMonitorCheck(connection); + // Execute the actions - if (actions!=null) { + if (actions != null) { + for (Action a : actions) { logger.debug("Action " + a.toString()); - for (int i = 0; i0) { + if (a.getTimeout() > 0) { try { Thread.sleep(a.getTimeout()); @@ -270,54 +320,48 @@ public void check(){ } } - JMXConnection connection = new JMXConnection(config.getUriJmxService()); // Monitor List monitor = a.getMonitor(); - if (monitor.contains("cpu")){ - // TODO: to be implemented. + if (monitor.contains("cpu")) { + try { + cpuMonitorChecker.monitor(); + } catch (IOException e) { + logger.error("Error while monitor CPU time ", e); + } } - if (monitor.contains("mem")){ + if (monitor.contains("mem")) { - MemoryChecker check = new MemoryCheckImp(connection,config.getApplicationDirectory()); try { - check.monitor(); + memoryChecker.monitor(); } catch (IOException e) { logger.error("Error while monitoring memory ", e); } - } + if (this.export){ + ProcessRun action = (ProcessRun) a; + EntrySummary entrySummary = new EntrySummary(action.getName(),""+i, + ""+sdf.format(timestamp),""+cpuMonitorChecker.getCpuCycle(), + ""+memoryChecker.getTotalMemory()); + entrySummary.setRecords(memoryChecker.getClassRecords()); + try { + exporterCSV.exportSummaryEntry(entrySummary); + exporterCSV.exportHeapSummaryEntries(entrySummary); + } catch (IOException e) { + logger.error("Export problem ", e); + } - + } } } - } - else - { + } else { System.out.println("Warning: no actions available to execute. Please configure it first."); } // Execute the actions and dumps the results - } - /** - * - */ - public void monitorCPU(){ - - } - - /** - * - */ - public void monitorMem(){ - - } - - public void getFinalReport(){ - } - public static void getMetrics(){ + public static void getMetrics() { String hostName = "localhost"; @@ -326,14 +370,14 @@ public static void getMetrics(){ JMXServiceURL u = null; try { u = new JMXServiceURL( - "service:jmx:rmi:///jndi/rmi://" + hostName + ":" + portNum + "/jmxrmi"); + "service:jmx:rmi:///jndi/rmi://" + hostName + ":" + portNum + "/jmxrmi"); } catch (MalformedURLException e) { e.printStackTrace(); } try { JMXConnector c = JMXConnectorFactory.connect(u); -//create object instances that will be used to get memory and operating system Mbean objects exposed by JMX; create variables for cpu time and system time before + //create object instances that will be used to get memory and operating system Mbean objects exposed by JMX; create variables for cpu time and system time before Object memoryMbean = null; Object osMbean = null; long cpuBefore = 0; @@ -344,17 +388,17 @@ public static void getMetrics(){ cpuBefore = 0; -// call the garbage collector before the test using the Memory Mbean + // call the garbage collector before the test using the Memory Mbean jmxc.getMBeanServerConnection().invoke(new ObjectName("java.lang:type=Memory"), "gc", null, null); -//create a loop to get values every second (optional) + //create a loop to get values every second (optional) for (int i = 0; i < samplesCount; i++) { -//get an instance of the HeapMemoryUsage Mbean + //get an instance of the HeapMemoryUsage Mbean memoryMbean = jmxc.getMBeanServerConnection().getAttribute(new ObjectName("java.lang:type=Memory"), "HeapMemoryUsage"); cd = (CompositeData) memoryMbean; -//get an instance of the OperatingSystem Mbean - osMbean = jmxc.getMBeanServerConnection().getAttribute(new ObjectName("java.lang:type=OperatingSystem"),"ProcessCpuTime"); + //get an instance of the OperatingSystem Mbean + osMbean = jmxc.getMBeanServerConnection().getAttribute(new ObjectName("java.lang:type=OperatingSystem"), "ProcessCpuTime"); System.out.println("Used memory: " + " " + cd.get("used") + " Used cpu: " + osMbean); //print memory usage tempMemory = tempMemory + Long.parseLong(cd.get("used").toString()); @@ -365,15 +409,13 @@ public static void getMetrics(){ System.out.println("Used NON HEAP memory: " + " " + cd.get("used") + " Used cpu: " + osMbean); //print memory usage - Thread.sleep(1000); //delay for one second } -//get system time and cpu time from last poll + //get system time and cpu time from last poll long cpuAfter = Long.parseLong(osMbean.toString()); - long cpuDiff = cpuAfter - cpuBefore; //find cpu time between our first and last jmx poll System.out.println("Cpu diff in milli seconds: " + cpuDiff / 1000000); //print cpu time in miliseconds System.out.println("average memory usage is: " + tempMemory / samplesCount);//print average memory usage @@ -384,23 +426,16 @@ public static void getMetrics(){ objectInstances.stream().forEach(instance -> System.out.println(instance)); MBeanInfo info = c.getMBeanServerConnection().getMBeanInfo(new ObjectName("java.lang:type=Memory")); - for (MBeanAttributeInfo element : info.getAttributes()) - { + for (MBeanAttributeInfo element : info.getAttributes()) { Object value; - if (element.isReadable()) - { - try - { + if (element.isReadable()) { + try { value = c.getMBeanServerConnection().getAttribute(new ObjectName("java.lang:type=Memory"), element.getName()); System.out.println(element.getName()); System.out.println(value.toString()); + } catch (Exception e) { } - catch (Exception e) - { - } - } - else - { + } else { } } /* @@ -505,10 +540,6 @@ public Main checkCPU(boolean checkCPU) { return this; } - public Main export(boolean export) { - this.export = export; - return this; - } public Main exportDirectory(String exportDirectory) { this.exportDirectory = exportDirectory; @@ -551,6 +582,17 @@ public void setConfig(YmlConfig config) { this.config = config; } + + public ExporterCSV getExporterCSV() { + return exporterCSV; + } + + public void setExporterCSV(ExporterCSV exporterCSV) { + this.exporterCSV = exporterCSV; + } + + + @Override public boolean equals(Object o) { if (o == this) @@ -570,14 +612,13 @@ public int hashCode() { @Override public String toString() { return "{" + - " check='" + isCheck() + "'" + - ", checkMemory='" + isCheckMemory() + "'" + - ", checkCPU='" + isCheckCPU() + "'" + - ", export='" + isExport() + "'" + - ", exportDirectory='" + getExportDirectory() + "'" + - "}"; + " check='" + isCheck() + "'" + + ", checkMemory='" + isCheckMemory() + "'" + + ", checkCPU='" + isCheckCPU() + "'" + + ", export='" + isExport() + "'" + + ", exportDirectory='" + getExportDirectory() + "'" + + "}"; } - } diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/conf/YmlConfig.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/conf/YmlConfig.java index 9abaa8b..fa68aac 100644 --- a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/conf/YmlConfig.java +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/conf/YmlConfig.java @@ -54,6 +54,7 @@ public class YmlConfig { */ private static final String ACTIONS2 = "actions"; + private boolean loaded = false; /** * @@ -99,6 +100,8 @@ public void load() throws FileNotFoundException { * @throws FileNotFoundException */ public void load(File f) throws FileNotFoundException { + if (loaded) + return; if (!f.exists()) { throw new FileNotFoundException(f.getAbsolutePath()); @@ -128,6 +131,7 @@ public void load(File f) throws FileNotFoundException { } logger.debug("Loading configurations complete. "); + loaded = true; } diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/cpu/CPUMonitor.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/cpu/CPUMonitor.java index 7df10e1..38b1de3 100644 --- a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/cpu/CPUMonitor.java +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/cpu/CPUMonitor.java @@ -32,5 +32,6 @@ public interface CPUMonitor { public void monitor() throws IOException; + public Long getCpuCycle() ; } diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/cpu/imp/CPUMonitorCheck.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/cpu/imp/CPUMonitorCheck.java new file mode 100644 index 0000000..92cf133 --- /dev/null +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/cpu/imp/CPUMonitorCheck.java @@ -0,0 +1,81 @@ +package com.bmdsoftware.monitor.resourcemonitor.cpu.imp; + +import com.bmdsoftware.monitor.resourcemonitor.Main; +import com.bmdsoftware.monitor.resourcemonitor.cpu.CPUMonitor; +import com.bmdsoftware.monitor.resourcemonitor.jmx.JMXConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import java.io.IOException; + +public class CPUMonitorCheck implements CPUMonitor { + + private static Logger logger = LoggerFactory.getLogger(CPUMonitorCheck.class); + + + private JMXConnection jmxConnection; + private Long cpuCycle; + + + public CPUMonitorCheck(JMXConnection jmxConnection) { + this.jmxConnection = jmxConnection; + } + + @Override + public void monitor() throws IOException { + + + JMXServiceURL url = this.jmxConnection.getJmxService(); + JMXConnector jmxc = JMXConnectorFactory.connect(url, null); + MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); + + Object osMbean = null; + + //get an instance of the OperatingSystem Mbean + try { + osMbean = jmxc.getMBeanServerConnection().getAttribute(new ObjectName("java.lang:type=OperatingSystem"),"ProcessCpuTime"); + long cpuAfter = Long.parseLong(osMbean.toString()); + + cpuCycle = cpuAfter; + + + } catch (MBeanException e) { + logger.error("Error while fetching MBean ", e); + } catch (AttributeNotFoundException e) { + logger.error("Error while fetching MBean attribute ", e); + } catch (InstanceNotFoundException e) { + logger.error("Error while fetching MBean instance ", e); + } catch (ReflectionException e) { + logger.error("Error while fetching MBean - reflection issue ", e); + } catch (MalformedObjectNameException e) { + logger.error("Error while fetching MBean - malformed object name ", e); + } + jmxc.close(); + } + + public JMXConnection getJmxConnection() { + return jmxConnection; + } + + public void setJmxConnection(JMXConnection jmxConnection) { + this.jmxConnection = jmxConnection; + } + + public Long getCpuCycle() { + return cpuCycle; + } + + public void setCpuCycle(Long cpuCycle) { + this.cpuCycle = cpuCycle; + } +} diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/export/EntrySummary.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/export/EntrySummary.java new file mode 100644 index 0000000..cc6b6b7 --- /dev/null +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/export/EntrySummary.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2019, BMD software + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.bmdsoftware.monitor.resourcemonitor.export; + +import org.gridkit.jvmtool.heapdump.HeapHistogram; + +import java.util.List; + +public class EntrySummary { + + + private String name; + private String iteration; + private String timestamp; + private String cpuTime; + private String totalMemory; + + private List records; + + public EntrySummary(String name, String iteration, String timestamp, String cpuTime, String totalMemory) { + this.name = name; + this.iteration = iteration; + this.timestamp = timestamp; + this.cpuTime = cpuTime; + this.totalMemory = totalMemory; + } + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIteration() { + return iteration; + } + + public void setIteration(String iteration) { + this.iteration = iteration; + } + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public String getCpuTime() { + return cpuTime; + } + + public void setCpuTime(String cpuTime) { + this.cpuTime = cpuTime; + } + + public String getTotalMemory() { + return totalMemory; + } + + public void setTotalMemory(String totalMemory) { + this.totalMemory = totalMemory; + } + + public List getRecords() { + return records; + } + + public void setRecords(List records) { + this.records = records; + } +} + + + diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/export/ExporterCSV.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/export/ExporterCSV.java new file mode 100644 index 0000000..0290228 --- /dev/null +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/export/ExporterCSV.java @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2019, BMD software + * All rights reserved. + *

+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +package com.bmdsoftware.monitor.resourcemonitor.export; + +import com.bmdsoftware.monitor.resourcemonitor.cpu.CPUMonitor; +import com.bmdsoftware.monitor.resourcemonitor.memory.MemoryChecker; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.gridkit.jvmtool.heapdump.HeapHistogram; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + + +/** + * Export Memory Summary to CSV format + */ +public class ExporterCSV { + + + private String exportFile; + + private String exportHeapFile; + + private boolean exportSummaryOpened = false; + private boolean exportHeapSummaryOpened = false; + + private CSVPrinter csvPrinterSummary = null; + private CSVPrinter csvPrinterHeapSummary = null; + + public ExporterCSV(String exportFile, String exportHeapFile ) { + this.exportFile = exportFile; + this.exportHeapFile = exportHeapFile; + } + + + public void exportSummary() throws IOException { + // [ActionName, ExecutionIteration, Timestamp, UsedMemory, CPUTime] + String[] HEADERS = { "ActionName", "ExecutionIteration", "Timestamp", "UsedMemory", "CPUTime"}; + if (!exportSummaryOpened) { + BufferedWriter writer = Files.newBufferedWriter(Paths.get(exportFile)); + csvPrinterSummary = new CSVPrinter(writer, CSVFormat.DEFAULT + .withHeader(HEADERS)); + } + } + + + public void exportSummaryEntry(EntrySummary entry) throws IOException { + // Pop last actions + csvPrinterSummary.printRecord(entry.getName(), entry.getIteration(), entry.getTimestamp(), + entry.getTotalMemory(), entry.getCpuTime()); + csvPrinterSummary.flush(); + + } + + + public void exportHeapSummary() throws IOException { + // [ActionName, ExecutionIteration, NameOfObject, ByteOrderBySize, Size, Count] + + String[] HEADERS = { "ActionName", "ExecutionIteration", "NameOfObject", "ByteOrderBySize", "Size", "Count"}; + if (!exportHeapSummaryOpened) { + BufferedWriter writer = Files.newBufferedWriter(Paths.get(exportHeapFile)); + csvPrinterHeapSummary = new CSVPrinter(writer, CSVFormat.DEFAULT + .withHeader(HEADERS)); + } + } + + public void exportHeapSummaryEntries(EntrySummary entry) throws IOException { + + for(HeapHistogram.ClassRecord r: entry.getRecords()){ + exportHeapSummaryEntry(entry, r); + } + csvPrinterHeapSummary.flush(); + + } + + public void exportHeapSummaryEntry(EntrySummary entry, HeapHistogram.ClassRecord record) throws IOException { + csvPrinterHeapSummary.printRecord(entry.getName(), entry.getIteration(), record.getClassName(), + record.getInstanceCount(), record.getInstanceCount()); + } + + + + public String getExportFile() { + return exportFile; + } + + public void setExportFile(String exportFile) { + this.exportFile = exportFile; + } + +} diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryAnalysisUtil.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryAnalysisUtil.java index 9e0fc1d..679b515 100644 --- a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryAnalysisUtil.java +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryAnalysisUtil.java @@ -56,7 +56,7 @@ public static void main(String [] args) throws Exception { heap = HeapFactory.createFastHeap(new File("c:/Users/bastiao/Projects/dicoogle/dicoogle/target/"+fileName)); reportStrings(); - printHistogram(); + printHistogram(1000); } @@ -75,7 +75,7 @@ public static void reportStrings() { /** * Create "jmap -histo" like class histogram from dump */ - public static void printHistogram() { + public static List printHistogram(Integer samplesNum) { StringCollector collector = new StringCollector(); HeapHistogram histo = new HeapHistogram(); collector.collect(heap, histo); @@ -84,11 +84,12 @@ public static void printHistogram() { Collections.sort(ht, HeapHistogram.BY_SIZE); TextTable tt = new TextTable(); int n = 0; - for(ClassRecord cr: ht.subList(0, 10)) { + for(ClassRecord cr: ht.subList(0, samplesNum)) { tt.addRow("" + (++n), " " + cr.getTotalSize(), " " + cr.getInstanceCount(), " " + cr.getClassName()); } System.out.println(tt.formatTextTableUnbordered(1000)); + return ht.subList(0, samplesNum); } /** diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryChecker.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryChecker.java index 7c34043..1e2edc3 100644 --- a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryChecker.java +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/MemoryChecker.java @@ -27,12 +27,16 @@ package com.bmdsoftware.monitor.resourcemonitor.memory; +import org.gridkit.jvmtool.heapdump.HeapHistogram; import org.netbeans.lib.profiler.heap.Heap; import java.io.IOException; +import java.util.List; public interface MemoryChecker { public void monitor() throws IOException ; public Heap getHeap(); + public Long getTotalMemory() ; + public List getClassRecords(); } diff --git a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/imp/MemoryCheckImp.java b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/imp/MemoryCheckImp.java index 852d09f..f2c9e86 100644 --- a/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/imp/MemoryCheckImp.java +++ b/src/main/java/com/bmdsoftware/monitor/resourcemonitor/memory/imp/MemoryCheckImp.java @@ -27,12 +27,16 @@ package com.bmdsoftware.monitor.resourcemonitor.memory.imp; +import com.bmdsoftware.monitor.resourcemonitor.cpu.imp.CPUMonitorCheck; import com.bmdsoftware.monitor.resourcemonitor.jmx.JMXConnection; import com.bmdsoftware.monitor.resourcemonitor.memory.MemoryAnalysisUtil; import com.bmdsoftware.monitor.resourcemonitor.memory.MemoryChecker; import com.sun.management.HotSpotDiagnosticMXBean; +import org.gridkit.jvmtool.heapdump.HeapHistogram; import org.netbeans.lib.profiler.heap.Heap; import org.netbeans.lib.profiler.heap.HeapFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; @@ -50,13 +54,12 @@ import javax.management.remote.JMXServiceURL; import java.io.File; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; +import java.util.Queue; import static com.bmdsoftware.monitor.resourcemonitor.memory.MemoryAnalysisUtil.printHistogram; -import static com.bmdsoftware.monitor.resourcemonitor.memory.MemoryAnalysisUtil.reportStrings; /** @@ -64,8 +67,14 @@ */ public class MemoryCheckImp implements MemoryChecker { + private static Logger logger = LoggerFactory.getLogger(MemoryCheckImp.class); + + private JMXConnection jmxConnection; private String nameHeapFile; + private List records; + + private Integer SAMPLE_COUNT = 3; private Long totalMemory; @@ -101,7 +110,7 @@ public void monitor() throws IOException { MemoryAnalysisUtil.heap = heap; - printHistogram(); + records = printHistogram(10); // get the memory composite information @@ -112,9 +121,9 @@ public void monitor() throws IOException { long tempMemory = 0; CompositeData cd = null; - cpuBefore = Long.parseLong("100"); + cpuBefore = Long.parseLong("0"); - int sampleCount = 10; + int sampleCount = SAMPLE_COUNT; // call the garbage collector before the test using the Memory Mbean try { @@ -129,7 +138,7 @@ public void monitor() throws IOException { e.printStackTrace(); } -//create a loop to get values every second (optional) + //create a loop to get values every second (optional) for (int i = 0; i < sampleCount; i++) { //get an instance of the HeapMemoryUsage Mbean @@ -165,11 +174,7 @@ public void monitor() throws IOException { //System.out.println("Used memory: " + " " + cd.get("used") + " Used cpu: " + osMbean); //print memory usage tempMemory = tempMemory + Long.parseLong(cd.get("used").toString()); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } + } //get system time and cpu time from last poll @@ -178,10 +183,14 @@ public void monitor() throws IOException { long cpuDiff = cpuAfter - cpuBefore; //find cpu time between our first and last jmx poll System.out.println("Cpu diff in milli seconds: " + cpuDiff / 1000000); //print cpu time in miliseconds System.out.println("average memory usage is: " + tempMemory / sampleCount);//print average memory usage + this.totalMemory = tempMemory / sampleCount; + jmxc.close(); + + } - public JMXConnection getJmxConnection() { + public JMXConnection getJmxConnection() { return jmxConnection; } @@ -198,12 +207,21 @@ public void setNameHeapFile(String nameHeapFile) { } - public Heap getHeap() { return heap; } + public Long getTotalMemory() { + return totalMemory; + } + + @Override + public List getClassRecords() { + return records; + } + + @Override public boolean equals(Object o) { if (this == o) return true;