diff --git a/doc/Documentation.md b/doc/Documentation.md index 53d7eaf..a26de44 100644 --- a/doc/Documentation.md +++ b/doc/Documentation.md @@ -63,6 +63,31 @@ USERNAME = Username which owns this jvm process DL = If !D is shown if the jvm detected a thread deadlock ``` +### JVM overview CSV mode ### +Command-line: `jvmtop.sh --csv` + +The CSV option removes the header and enables easy and parsing (e.g. in R). + +``` +PID,MAIN-CLASS,HPCUR,HPMAX,NHCUR,NHMAX,CPU,GC,VM,USERNAME,#T,DL +3370,wrapperSimpleApp,165m,455m,109m,176m,0.12%,0.00%,S6U37,web,21 +27338,WatchdogManager,11m,28m,23m,130m,0.00%,0.00%,6U37 web,,31 +19187,m.jvmtop.JvmTop,20m,3544m,13m,130m,0.93%,0.47%,S6U37,web,20 +16733,artup.Bootstrap,159m,455m,166m,304m,0.12%,0.00%,S6U37,web,46 +``` + +### JVM overview timestamp (+ optional CSV) mode ### +Command-line: `jvmtop.sh --ts --csv` + +The timestamp option is a `new Date().toTime()` command for continuous monitoring. + +``` +PID,MAIN-CLASS,HPCUR,HPMAX,NHCUR,NHMAX,CPU,GC,VM,USERNAME,#T,DL,TS +3370,wrapperSimpleApp,165m,455m,109m,176m,0.12%,0.00%,S6U37,web,21,1480337426048 +27338,WatchdogManager,11m,28m,23m,130m,0.00%,0.00%,6U37 web,,31,1480337426048 +19187,m.jvmtop.JvmTop,20m,3544m,13m,130m,0.93%,0.47%,S6U37,web,20,1480337426048 +16733,artup.Bootstrap,159m,455m,166m,304m,0.12%,0.00%,S6U37,web,46,1480337426048 +``` ### Detail mode (Single-VM monitoring) ### diff --git a/src/main/java/com/jvmtop/JvmTop.java b/src/main/java/com/jvmtop/JvmTop.java index dd4165c..224b2bc 100644 --- a/src/main/java/com/jvmtop/JvmTop.java +++ b/src/main/java/com/jvmtop/JvmTop.java @@ -20,6 +20,10 @@ */ package com.jvmtop; +import com.jvmtop.view.*; +import joptsimple.OptionParser; +import joptsimple.OptionSet; + import java.io.BufferedOutputStream; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -33,14 +37,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import joptsimple.OptionParser; -import joptsimple.OptionSet; - -import com.jvmtop.view.ConsoleView; -import com.jvmtop.view.VMDetailView; -import com.jvmtop.view.VMOverviewView; -import com.jvmtop.view.VMProfileView; - /** * JvmTop entry point class. * @@ -73,8 +69,9 @@ public class JvmTop private int maxIterations_ = -1; private static Logger logger; + private static DisplayOptions opts; - private static OptionParser createOptionParser() + private static OptionParser createOptionParser() { OptionParser parser = new OptionParser(); parser.acceptsAll(Arrays.asList(new String[] { "help", "?", "h" }), @@ -112,6 +109,9 @@ private static OptionParser createOptionParser() "sets displayed thread name length in detail mode (defaults to 30)") .withRequiredArg().ofType(Integer.class); + parser.accepts("ts", "Display timestamp"); + parser.accepts("csv", "Use CSV output"); + return parser; } @@ -202,6 +202,20 @@ public static void main(String[] args) throws Exception threadNameWidth = (Integer) a.valueOf("threadnamewidth"); } + boolean isCsv = false; + if (a.has("csv")) + { + isCsv = true; + } + + boolean isTS = false; + if (a.has("ts")) + { + isTS = true; + } + + opts = new DisplayOptions(isCsv, isTS, width); + if (sysInfoOption) { outputSystemProps(); @@ -213,17 +227,17 @@ public static void main(String[] args) throws Exception jvmTop.setMaxIterations(iterations); if (pid == null) { - jvmTop.run(new VMOverviewView(width)); + jvmTop.run(new VMOverviewView(opts)); } else { if (profileMode) { - jvmTop.run(new VMProfileView(pid, width)); + jvmTop.run(new VMProfileView(pid, opts)); } else { - VMDetailView vmDetailView = new VMDetailView(pid, width); + VMDetailView vmDetailView = new VMDetailView(pid, opts); vmDetailView.setDisplayedThreadLimit(threadLimitEnabled); if (threadlimit != null) { @@ -300,7 +314,7 @@ protected void run(ConsoleView view) throws Exception { clearTerminal(); } - printTopBar(); + if(!opts.isCSV()) printTopBar(); view.printView(); System.out.flush(); iterations++; diff --git a/src/main/java/com/jvmtop/view/AbstractConsoleView.java b/src/main/java/com/jvmtop/view/AbstractConsoleView.java index 6f4de12..1bcabcc 100644 --- a/src/main/java/com/jvmtop/view/AbstractConsoleView.java +++ b/src/main/java/com/jvmtop/view/AbstractConsoleView.java @@ -49,12 +49,12 @@ public abstract class AbstractConsoleView implements ConsoleView /** * */ - public AbstractConsoleView(Integer width) + public AbstractConsoleView(DisplayOptions opts) { super(); - if (width == null) width = MIN_WIDTH; - if (width < MIN_WIDTH) width = MIN_WIDTH; - this.width = width; + if (opts.getWidth() == null) width = MIN_WIDTH; + else if (opts.getWidth() < MIN_WIDTH) width = MIN_WIDTH; + else this.width = opts.getWidth(); } /** diff --git a/src/main/java/com/jvmtop/view/DisplayOptions.java b/src/main/java/com/jvmtop/view/DisplayOptions.java new file mode 100644 index 0000000..7129b09 --- /dev/null +++ b/src/main/java/com/jvmtop/view/DisplayOptions.java @@ -0,0 +1,38 @@ +package com.jvmtop.view; + + +public class DisplayOptions { + private boolean isCSV = false; + private boolean displayTS = false; + private Integer width = 0; + + public DisplayOptions(boolean isCSV, boolean displayTS, Integer width) { + this.isCSV = isCSV; + this.displayTS = displayTS; + this.width = width; + } + + public boolean isCSV() { + return isCSV; + } + + public void setCSV(boolean CSV) { + isCSV = CSV; + } + + public boolean isDisplayTS() { + return displayTS; + } + + public void setDisplayTS(boolean displayTS) { + this.displayTS = displayTS; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } +} diff --git a/src/main/java/com/jvmtop/view/VMDetailView.java b/src/main/java/com/jvmtop/view/VMDetailView.java index 21a40f0..7663b87 100644 --- a/src/main/java/com/jvmtop/view/VMDetailView.java +++ b/src/main/java/com/jvmtop/view/VMDetailView.java @@ -21,17 +21,13 @@ package com.jvmtop.view; -import java.lang.management.ThreadInfo; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - import com.jvmtop.monitor.VMInfo; import com.jvmtop.monitor.VMInfoState; import com.jvmtop.openjdk.tools.LocalVirtualMachine; +import java.lang.management.ThreadInfo; +import java.util.*; + /** * "detail" view, printing detail metrics of a specific jvm. * Also printing the top threads (based on the current CPU usage) @@ -55,9 +51,9 @@ public class VMDetailView extends AbstractConsoleView //TODO: refactor private Map previousThreadCPUMillis = new HashMap(); - public VMDetailView(int vmid, Integer width) throws Exception + public VMDetailView(int vmid, DisplayOptions opts) throws Exception { - super(width); + super(opts); LocalVirtualMachine localVirtualMachine = LocalVirtualMachine .getLocalVirtualMachine(vmid); vmInfo_ = VMInfo.processNewVM(localVirtualMachine, vmid); diff --git a/src/main/java/com/jvmtop/view/VMOverviewView.java b/src/main/java/com/jvmtop/view/VMOverviewView.java index 6f8d862..25e8844 100644 --- a/src/main/java/com/jvmtop/view/VMOverviewView.java +++ b/src/main/java/com/jvmtop/view/VMOverviewView.java @@ -20,18 +20,13 @@ */ package com.jvmtop.view; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - import com.jvmtop.monitor.VMInfo; import com.jvmtop.monitor.VMInfoState; import com.jvmtop.openjdk.tools.LocalVirtualMachine; +import java.util.*; +import java.util.Map.Entry; + /** * "overview" view, providing the most-important metrics of all accessible jvms in a top-like manner. * @@ -40,13 +35,14 @@ */ public class VMOverviewView extends AbstractConsoleView { - + private DisplayOptions opts; private List vmInfoList = new ArrayList(); private Map vmMap = new HashMap(); - public VMOverviewView(Integer width) { - super(width); + public VMOverviewView(DisplayOptions opts) { + super(opts); + this.opts = opts; } public void printView() throws Exception @@ -62,8 +58,7 @@ public void printView() throws Exception for (VMInfo vmInfo : vmInfoList) { - if (vmInfo.getState() == VMInfoState.ATTACHED -) + if (vmInfo.getState() == VMInfoState.ATTACHED) { printVM(vmInfo); } @@ -71,19 +66,20 @@ else if (vmInfo.getState() == VMInfoState.ATTACHED_UPDATE_ERROR) { System.out .printf( - "%5d %-15.15s [ERROR: Could not fetch telemetries (Process DEAD?)] %n", + getErrorFormat("%5d %-15.15s", "[ERROR: Could not fetch telemetries (Process DEAD?)]"), vmInfo.getId(), getEntryPointClass(vmInfo.getDisplayName())); } else if (vmInfo.getState() == VMInfoState.ERROR_DURING_ATTACH) { - System.out.printf("%5d %-15.15s [ERROR: Could not attach to VM] %n", + System.out.printf( + getErrorFormat("%5d %-15.15s","[ERROR: Could not attach to VM]"), vmInfo.getId(), getEntryPointClass(vmInfo.getDisplayName())); } else if (vmInfo.getState() == VMInfoState.CONNECTION_REFUSED) { System.out.printf( - "%5d %-15.15s [ERROR: Connection refused/access denied] %n", + getErrorFormat("%5d %-15.15s","[ERROR: Connection refused/access denied]"), vmInfo.getId(), getEntryPointClass(vmInfo.getDisplayName())); } @@ -104,8 +100,6 @@ private String getEntryPointClass(String name) } /** - * @param localvm - * @param vmid * @param vmInfo * @return * @throws Exception @@ -119,16 +113,27 @@ private void printVM(VMInfo vmInfo) throws Exception deadlockState = "!D"; } - System.out - .printf( - "%5d %-15.15s %5s %5s %5s %5s %5.2f%% %5.2f%% %-5.5s %8.8s %4d %2.2s%n", - vmInfo.getId(), getEntryPointClass(vmInfo.getDisplayName()), - toMB(vmInfo.getHeapUsed()), toMB(vmInfo.getHeapMax()), - toMB(vmInfo.getNonHeapUsed()), toMB(vmInfo.getNonHeapMax()), - vmInfo.getCpuLoad() * 100, vmInfo.getGcLoad() * 100, - vmInfo.getVMVersion(), vmInfo.getOSUser(), vmInfo.getThreadCount(), - deadlockState); - + if(opts.isDisplayTS()) { + System.out + .printf( + getFormat(), + vmInfo.getId(), getEntryPointClass(vmInfo.getDisplayName()), + toMB(vmInfo.getHeapUsed()), toMB(vmInfo.getHeapMax()), + toMB(vmInfo.getNonHeapUsed()), toMB(vmInfo.getNonHeapMax()), + vmInfo.getCpuLoad() * 100, vmInfo.getGcLoad() * 100, + vmInfo.getVMVersion(), vmInfo.getOSUser(), vmInfo.getThreadCount(), + deadlockState, new Date().getTime()); + }else { + System.out + .printf( + getFormat(), + vmInfo.getId(), getEntryPointClass(vmInfo.getDisplayName()), + toMB(vmInfo.getHeapUsed()), toMB(vmInfo.getHeapMax()), + toMB(vmInfo.getNonHeapUsed()), toMB(vmInfo.getNonHeapMax()), + vmInfo.getCpuLoad() * 100, vmInfo.getGcLoad() * 100, + vmInfo.getVMVersion(), vmInfo.getOSUser(), vmInfo.getThreadCount(), + deadlockState); + } } /** @@ -143,11 +148,6 @@ private void updateVMs(List vmList) throws Exception } } - /** - * @param vmMap - * @param vmMap - * @param set - */ private void scanForNewVMs() { Map machines = LocalVirtualMachine @@ -173,9 +173,68 @@ private void scanForNewVMs() */ private void printHeader() { - System.out.printf("%5s %-15.15s %5s %5s %5s %5s %6s %6s %5s %8s %4s %2s%n", - "PID", "MAIN-CLASS", "HPCUR", "HPMAX", "NHCUR", "NHMAX", "CPU", "GC", - "VM", "USERNAME", "#T", "DL"); + if(opts.isDisplayTS()){ + System.out.printf(getHeader(), + "PID", "MAIN-CLASS", "HPCUR", "HPMAX", "NHCUR", "NHMAX", "CPU", "GC", + "VM", "USERNAME", "#T", "DL", "TS" ); + }else { + System.out.printf(getHeader(), + "PID", "MAIN-CLASS", "HPCUR", "HPMAX", "NHCUR", "NHMAX", "CPU", "GC", + "VM", "USERNAME", "#T", "DL"); + } } -} + private String getFormat(){ + String format = "%5d %-15.15s %5s %5s %5s %5s %5.2f%% %5.2f%% %-5.5s %8.8s %4d %2.2s"; + return format(format); + } + + private String getHeader(){ + String format = "%5s %-15.15s %5s %5s %5s %5s %6s %6s %5s %8s %4s %2s"; + return format(format); + } + + private String getErrorFormat(String format, String errorMsg){ + String res = format(format, false, false); + return res + errorMsg + "%n"; + } + + private String format(String _default){ + return format(_default, true, true); + } + + private String removePadding(String format){ + return format.replaceAll("\\d","").replace("-","").replace(".",""); + } + + /** + * Format printf statement + * @param _default default statement including padding + * @param c add line for TS (N/A for error) + * @param n add newline (N/A for error) + * @return + */ + private String format(String _default, boolean c, boolean n){ + String format = _default; + String formatCsv = removePadding(format); + if(opts.isDisplayTS()){ + if(opts.isCSV()){ + format = formatCsv; + if(c) format += " %s"; + if(n) format += "%n"; + }else { + if(c) format += " %5s"; + if(n) format += "%n"; + } + } else{ + if(opts.isCSV()) format = formatCsv; + if(n) format += "%n"; + } + if(opts.isCSV()){ + format = format.replace(" ", ","); + //if(format.endsWith(",%n")) format.replace(",%n","%n"); + } + return format; + } + +} \ No newline at end of file diff --git a/src/main/java/com/jvmtop/view/VMProfileView.java b/src/main/java/com/jvmtop/view/VMProfileView.java index aba76ac..3db606a 100644 --- a/src/main/java/com/jvmtop/view/VMProfileView.java +++ b/src/main/java/com/jvmtop/view/VMProfileView.java @@ -20,14 +20,14 @@ */ package com.jvmtop.view; -import java.util.Iterator; - import com.jvmtop.monitor.VMInfo; import com.jvmtop.monitor.VMInfoState; import com.jvmtop.openjdk.tools.LocalVirtualMachine; import com.jvmtop.profiler.CPUSampler; import com.jvmtop.profiler.MethodStats; +import java.util.Iterator; + /** * CPU sampling-based profiler view which shows methods with top CPU usage. * @@ -41,9 +41,9 @@ public class VMProfileView extends AbstractConsoleView private VMInfo vmInfo_; - public VMProfileView(int vmid, Integer width) throws Exception + public VMProfileView(int vmid, DisplayOptions opts) throws Exception { - super(width); + super(opts); LocalVirtualMachine localVirtualMachine = LocalVirtualMachine .getLocalVirtualMachine(vmid); vmInfo_ = VMInfo.processNewVM(localVirtualMachine, vmid);