diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/JvmItemVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/JvmItemVO.java new file mode 100644 index 000000000..4c9b89006 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/JvmItemVO.java @@ -0,0 +1,46 @@ +package com.taobao.arthas.core.command.model; + +/** + * Key/value/desc + * @author gongdewei 2020/4/24 + */ +public class JvmItemVO { + private String name; + private Object value; + private String desc; + + public JvmItemVO(String name, Object value, String desc) { + this.name = name; + this.value = value; + this.desc = desc; + } + + public JvmItemVO(String name, Object value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/JvmModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/JvmModel.java new file mode 100644 index 000000000..dffc53c3f --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/model/JvmModel.java @@ -0,0 +1,48 @@ +package com.taobao.arthas.core.command.model; + +import java.util.*; + +/** + * Model of 'jvm' command + * + * @author gongdewei 2020/4/24 + */ +public class JvmModel extends ResultModel { + + private Map> jvmInfo; + + public JvmModel() { + jvmInfo = Collections.synchronizedMap(new LinkedHashMap>()); + } + + @Override + public String getType() { + return "jvm"; + } + + public JvmModel addItem(String group, String name, Object value) { + this.addItem(group, name, value, null); + return this; + } + + public JvmModel addItem(String group, String name, Object value, String desc) { + this.group(group).add(new JvmItemVO(name, value, desc)); + return this; + } + + public List group(String group) { + synchronized (this) { + List list = jvmInfo.get(group); + if (list == null) { + list = new ArrayList(); + jvmInfo.put(group, list); + } + return list; + } + } + + public Map> getJvmInfo() { + return jvmInfo; + } + +} diff --git a/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java b/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java index 7f3f295c2..7ff325693 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/monitor200/JvmCommand.java @@ -1,25 +1,21 @@ package com.taobao.arthas.core.command.monitor200; import com.taobao.arthas.core.command.Constants; +import com.taobao.arthas.core.command.model.GcInfoVO; +import com.taobao.arthas.core.command.model.JvmModel; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; -import com.taobao.arthas.core.util.StringUtils; -import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.middleware.cli.annotations.Description; import com.taobao.middleware.cli.annotations.Name; import com.taobao.middleware.cli.annotations.Summary; -import com.taobao.text.Decoration; -import com.taobao.text.ui.Element; -import com.taobao.text.ui.TableElement; -import com.taobao.text.util.RenderUtil; import java.lang.management.*; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; - -import static com.taobao.text.ui.Element.label; +import java.util.LinkedHashMap; +import java.util.Map; /** * JVM info command @@ -43,50 +39,41 @@ public class JvmCommand extends AnnotatedCommand { @Override public void process(CommandProcess process) { - RowAffect affect = new RowAffect(); - TableElement table = new TableElement(2, 5).leftCellPadding(1).rightCellPadding(1); - table.row(true, label("RUNTIME").style(Decoration.bold.bold())); - drawRuntimeTable(table); - table.row("", ""); - table.row(true, label("CLASS-LOADING").style(Decoration.bold.bold())); - drawClassLoadingTable(table); - table.row("", ""); - table.row(true, label("COMPILATION").style(Decoration.bold.bold())); - drawCompilationTable(table); + + JvmModel jvmModel = new JvmModel(); + + addRuntimeInfo(jvmModel); + + addClassLoading(jvmModel); + + addCompilation(jvmModel); if (!garbageCollectorMXBeans.isEmpty()) { - table.row("", ""); - table.row(true, label("GARBAGE-COLLECTORS").style(Decoration.bold.bold())); - drawGarbageCollectorsTable(table); + addGarbageCollectors(jvmModel); } if (!memoryManagerMXBeans.isEmpty()) { - table.row("", ""); - table.row(true, label("MEMORY-MANAGERS").style(Decoration.bold.bold())); - drawMemoryManagersTable(table); + addMemoryManagers(jvmModel); } - table.row("", ""); - table.row(true, label("MEMORY").style(Decoration.bold.bold())); - drawMemoryTable(table); - table.row("", ""); - table.row(true, label("OPERATING-SYSTEM").style(Decoration.bold.bold())); - drawOperatingSystemMXBeanTable(table); - table.row("", ""); - table.row(true, label("THREAD").style(Decoration.bold.bold())); - drawThreadTable(table); - table.row("", ""); - table.row(true, label("FILE-DESCRIPTOR").style(Decoration.bold.bold())); - drawFileDescriptorTable(table); - process.write(RenderUtil.render(table, process.width())); - process.write(affect.toString()).write("\n"); + addMemory(jvmModel); + + addOperatingSystem(jvmModel); + + addThread(jvmModel); + + addFileDescriptor(jvmModel); + + process.appendResult(jvmModel); process.end(); } - private void drawFileDescriptorTable(TableElement table) { - table.row("MAX-FILE-DESCRIPTOR-COUNT", "" + invokeFileDescriptor(operatingSystemMXBean, "getMaxFileDescriptorCount")) - .row("OPEN-FILE-DESCRIPTOR-COUNT", "" + invokeFileDescriptor(operatingSystemMXBean, "getOpenFileDescriptorCount")); + private void addFileDescriptor(JvmModel jvmModel) { + String group = "FILE-DESCRIPTOR"; + jvmModel.addItem(group,"MAX-FILE-DESCRIPTOR-COUNT", invokeFileDescriptor(operatingSystemMXBean, "getMaxFileDescriptorCount")) + .addItem(group,"OPEN-FILE-DESCRIPTOR-COUNT", invokeFileDescriptor(operatingSystemMXBean, "getOpenFileDescriptorCount")); } + private long invokeFileDescriptor(OperatingSystemMXBean os, String name) { try { final Method method = os.getClass().getDeclaredMethod(name); @@ -96,137 +83,111 @@ public class JvmCommand extends AnnotatedCommand { return -1; } } - private String toCol(Collection strings) { - final StringBuilder colSB = new StringBuilder(); - if (strings.isEmpty()) { - colSB.append("[]"); - } else { - for (String str : strings) { - colSB.append(str).append("\n"); - } - } - return colSB.toString(); - } - private String toCol(String... stringArray) { - final StringBuilder colSB = new StringBuilder(); - if (null == stringArray - || stringArray.length == 0) { - colSB.append("[]"); - } else { - for (String str : stringArray) { - colSB.append(str).append("\n"); - } - } - return colSB.toString(); - } - - private Element drawRuntimeTable(TableElement table) { + private void addRuntimeInfo(JvmModel jvmModel) { String bootClassPath = ""; try { bootClassPath = runtimeMXBean.getBootClassPath(); } catch (Exception e) { // under jdk9 will throw UnsupportedOperationException, ignore } - table.row("MACHINE-NAME", runtimeMXBean.getName()) - .row("JVM-START-TIME", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(runtimeMXBean.getStartTime()))) - .row("MANAGEMENT-SPEC-VERSION", runtimeMXBean.getManagementSpecVersion()) - .row("SPEC-NAME", runtimeMXBean.getSpecName()) - .row("SPEC-VENDOR", runtimeMXBean.getSpecVendor()) - .row("SPEC-VERSION", runtimeMXBean.getSpecVersion()) - .row("VM-NAME", runtimeMXBean.getVmName()) - .row("VM-VENDOR", runtimeMXBean.getVmVendor()) - .row("VM-VERSION", runtimeMXBean.getVmVersion()) - .row("INPUT-ARGUMENTS", toCol(runtimeMXBean.getInputArguments())) - .row("CLASS-PATH", runtimeMXBean.getClassPath()) - .row("BOOT-CLASS-PATH", bootClassPath) - .row("LIBRARY-PATH", runtimeMXBean.getLibraryPath()); - - return table; + String group = "RUNTIME"; + jvmModel.addItem(group,"MACHINE-NAME", runtimeMXBean.getName()); + jvmModel.addItem(group, "JVM-START-TIME", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(runtimeMXBean.getStartTime()))); + jvmModel.addItem(group, "MANAGEMENT-SPEC-VERSION", runtimeMXBean.getManagementSpecVersion()); + jvmModel.addItem(group, "SPEC-NAME", runtimeMXBean.getSpecName()); + jvmModel.addItem(group, "SPEC-VENDOR", runtimeMXBean.getSpecVendor()); + jvmModel.addItem(group, "SPEC-VERSION", runtimeMXBean.getSpecVersion()); + jvmModel.addItem(group, "VM-NAME", runtimeMXBean.getVmName()); + jvmModel.addItem(group, "VM-VENDOR", runtimeMXBean.getVmVendor()); + jvmModel.addItem(group, "VM-VERSION", runtimeMXBean.getVmVersion()); + jvmModel.addItem(group, "INPUT-ARGUMENTS", runtimeMXBean.getInputArguments()); + jvmModel.addItem(group, "CLASS-PATH", runtimeMXBean.getClassPath()); + jvmModel.addItem(group, "BOOT-CLASS-PATH", bootClassPath); + jvmModel.addItem(group, "LIBRARY-PATH", runtimeMXBean.getLibraryPath()); } - private Element drawClassLoadingTable(TableElement table) { - table.row("LOADED-CLASS-COUNT", "" + classLoadingMXBean.getLoadedClassCount()) - .row("TOTAL-LOADED-CLASS-COUNT", "" + classLoadingMXBean.getTotalLoadedClassCount()) - .row("UNLOADED-CLASS-COUNT", "" + classLoadingMXBean.getUnloadedClassCount()) - .row("IS-VERBOSE", "" + classLoadingMXBean.isVerbose()); - - return table; + private void addClassLoading(JvmModel jvmModel) { + String group = "CLASS-LOADING"; + jvmModel.addItem(group, "LOADED-CLASS-COUNT", classLoadingMXBean.getLoadedClassCount()); + jvmModel.addItem(group, "TOTAL-LOADED-CLASS-COUNT", classLoadingMXBean.getTotalLoadedClassCount()); + jvmModel.addItem(group, "UNLOADED-CLASS-COUNT", classLoadingMXBean.getUnloadedClassCount()); + jvmModel.addItem(group, "IS-VERBOSE", classLoadingMXBean.isVerbose()); } - private Element drawCompilationTable(TableElement table) { - table.row("NAME", compilationMXBean.getName()); - + private void addCompilation(JvmModel jvmModel) { + String group = "COMPILATION"; + jvmModel.addItem(group, "NAME", compilationMXBean.getName()); if (compilationMXBean.isCompilationTimeMonitoringSupported()) { - table.row("TOTAL-COMPILE-TIME", compilationMXBean.getTotalCompilationTime() + "(ms)"); - + jvmModel.addItem(group, "TOTAL-COMPILE-TIME", compilationMXBean.getTotalCompilationTime(), "time (ms)"); } - return table; } - private Element drawGarbageCollectorsTable(TableElement table) { - for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMXBeans) { - table.row(garbageCollectorMXBean.getName() + "\n[count/time]", - garbageCollectorMXBean.getCollectionCount() + "/" + garbageCollectorMXBean.getCollectionTime() + "(ms)"); - } + private void addGarbageCollectors(JvmModel jvmModel) { + String group = "GARBAGE-COLLECTORS"; + for (GarbageCollectorMXBean gcMXBean : garbageCollectorMXBeans) { + Map gcInfo = new LinkedHashMap(); + gcInfo.put("name", gcMXBean.getName()); + gcInfo.put("collectionCount", gcMXBean.getCollectionCount()); + gcInfo.put("collectionTime", gcMXBean.getCollectionTime()); - return table; + jvmModel.addItem(group, gcMXBean.getName(), gcInfo, "count/time (ms)"); + } } - private Element drawMemoryManagersTable(TableElement table) { + private void addMemoryManagers(JvmModel jvmModel) { + String group = "MEMORY-MANAGERS"; for (final MemoryManagerMXBean memoryManagerMXBean : memoryManagerMXBeans) { if (memoryManagerMXBean.isValid()) { final String name = memoryManagerMXBean.isValid() ? memoryManagerMXBean.getName() : memoryManagerMXBean.getName() + "(Invalid)"; - - - table.row(name, toCol(memoryManagerMXBean.getMemoryPoolNames())); + jvmModel.addItem(group, name, memoryManagerMXBean.getMemoryPoolNames()); } } - - return table; } - private Element drawMemoryTable(TableElement table) { + private void addMemory(JvmModel jvmModel) { + String group = "MEMORY"; MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); - table.row("HEAP-MEMORY-USAGE\n[committed/init/max/used]", - formatMemoryByte(heapMemoryUsage.getCommitted()) - + "/" + formatMemoryByte(heapMemoryUsage.getInit()) - + "/" + formatMemoryByte(heapMemoryUsage.getMax()) - + "/" + formatMemoryByte(heapMemoryUsage.getUsed()) - ); + Map heapMemoryInfo = getMemoryUsageInfo("heap", heapMemoryUsage); + jvmModel.addItem(group, "HEAP-MEMORY-USAGE", heapMemoryInfo, "memory in bytes"); + MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); - table.row("NO-HEAP-MEMORY-USAGE\n[committed/init/max/used]", - formatMemoryByte(nonHeapMemoryUsage.getCommitted()) - + "/" + formatMemoryByte(nonHeapMemoryUsage.getInit()) - + "/" + formatMemoryByte(nonHeapMemoryUsage.getMax()) - + "/" + formatMemoryByte(nonHeapMemoryUsage.getUsed()) - ); - - table.row("PENDING-FINALIZE-COUNT", "" + memoryMXBean.getObjectPendingFinalizationCount()); - return table; + Map nonheapMemoryInfo = getMemoryUsageInfo("nonheap", nonHeapMemoryUsage); + jvmModel.addItem(group,"NO-HEAP-MEMORY-USAGE", nonheapMemoryInfo, "memory in bytes"); + + jvmModel.addItem(group,"PENDING-FINALIZE-COUNT", memoryMXBean.getObjectPendingFinalizationCount()); } - private String formatMemoryByte(long bytes){ - return String.format("%s(%s)",bytes, StringUtils.humanReadableByteCount(bytes)); + + private Map getMemoryUsageInfo(String name, MemoryUsage heapMemoryUsage) { + Map memoryInfo = new LinkedHashMap(); + memoryInfo.put("name", name); + memoryInfo.put("init", heapMemoryUsage.getInit()); + memoryInfo.put("used", heapMemoryUsage.getUsed()); + memoryInfo.put("committed", heapMemoryUsage.getCommitted()); + memoryInfo.put("max", heapMemoryUsage.getMax()); + return memoryInfo; } - private Element drawOperatingSystemMXBeanTable(TableElement table) { - table.row("OS", operatingSystemMXBean.getName()).row("ARCH", operatingSystemMXBean.getArch()) - .row("PROCESSORS-COUNT", "" + operatingSystemMXBean.getAvailableProcessors()) - .row("LOAD-AVERAGE", "" + operatingSystemMXBean.getSystemLoadAverage()) - .row("VERSION", operatingSystemMXBean.getVersion()); - return table; + private void addOperatingSystem(JvmModel jvmModel) { + String group = "OPERATING-SYSTEM"; + jvmModel.addItem(group,"OS", operatingSystemMXBean.getName()) + .addItem(group,"ARCH", operatingSystemMXBean.getArch()) + .addItem(group,"PROCESSORS-COUNT", operatingSystemMXBean.getAvailableProcessors()) + .addItem(group,"LOAD-AVERAGE", operatingSystemMXBean.getSystemLoadAverage()) + .addItem(group,"VERSION", operatingSystemMXBean.getVersion()); } - private Element drawThreadTable(TableElement table) { - table.row("COUNT", "" + threadMXBean.getThreadCount()) - .row("DAEMON-COUNT", "" + threadMXBean.getDaemonThreadCount()) - .row("PEAK-COUNT", "" + threadMXBean.getPeakThreadCount()) - .row("STARTED-COUNT", "" + threadMXBean.getTotalStartedThreadCount()) - .row("DEADLOCK-COUNT","" + getDeadlockedThreadsCount(threadMXBean)); - return table; + private void addThread(JvmModel jvmModel) { + String group = "THREAD"; + jvmModel.addItem(group, "COUNT", threadMXBean.getThreadCount()) + .addItem(group, "DAEMON-COUNT", threadMXBean.getDaemonThreadCount()) + .addItem(group, "PEAK-COUNT", threadMXBean.getPeakThreadCount()) + .addItem(group, "STARTED-COUNT", threadMXBean.getTotalStartedThreadCount()) + .addItem(group, "DEADLOCK-COUNT",getDeadlockedThreadsCount(threadMXBean)); } + private int getDeadlockedThreadsCount(ThreadMXBean threads) { final long[] ids = threads.findDeadlockedThreads(); if (ids == null) { diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/JvmView.java b/core/src/main/java/com/taobao/arthas/core/command/view/JvmView.java new file mode 100644 index 000000000..34a2952d3 --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/command/view/JvmView.java @@ -0,0 +1,120 @@ +package com.taobao.arthas.core.command.view; + +import com.taobao.arthas.core.command.model.JvmModel; +import com.taobao.arthas.core.command.model.JvmItemVO; +import com.taobao.arthas.core.shell.command.CommandProcess; +import com.taobao.arthas.core.util.StringUtils; +import com.taobao.text.Decoration; +import com.taobao.text.ui.TableElement; +import com.taobao.text.util.RenderUtil; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.taobao.text.ui.Element.label; + +/** + * View of 'jvm' command + * + * @author gongdewei 2020/4/24 + */ +public class JvmView extends ResultView { + + @Override + public void draw(CommandProcess process, JvmModel result) { + TableElement table = new TableElement(2, 5).leftCellPadding(1).rightCellPadding(1); + + for (Map.Entry> entry : result.getJvmInfo().entrySet()) { + String group = entry.getKey(); + List items = entry.getValue(); + + table.row(true, label(group).style(Decoration.bold.bold())); + for (JvmItemVO item : items) { + String valueStr; + if (item.getValue() instanceof Map && item.getName().endsWith("MEMORY-USAGE")) { + valueStr = renderMemoryUsage((Map) item.getValue()); + } else { + valueStr = renderItemValue(item.getValue()); + } + if (item.getDesc() != null) { + table.row(item.getName() + "\n[" + item.getDesc() + "]", valueStr); + } else { + table.row(item.getName(), valueStr); + } + } + table.row("", ""); + } + + process.write(RenderUtil.render(table, process.width())); + } + + private String renderCountTime(long[] value) { + //count/time + return value[0] + "/" + value[1]; + } + + private String renderItemValue(Object value) { + if (value == null) { + return "null"; + } + if (value instanceof Collection) { + return renderCollectionValue((Collection) value); + } else if (value instanceof String[]) { + return renderArrayValue((String[]) value); + } else if (value instanceof Map) { + return renderMapValue((Map) value); + } + return String.valueOf(value); + } + + private String renderCollectionValue(Collection strings) { + final StringBuilder colSB = new StringBuilder(); + if (strings.isEmpty()) { + colSB.append("[]"); + } else { + for (String str : strings) { + colSB.append(str).append("\n"); + } + } + return colSB.toString(); + } + + private String renderArrayValue(String... stringArray) { + final StringBuilder colSB = new StringBuilder(); + if (null == stringArray + || stringArray.length == 0) { + colSB.append("[]"); + } else { + for (String str : stringArray) { + colSB.append(str).append("\n"); + } + } + return colSB.toString(); + } + + private String renderMapValue(Map valueMap) { + final StringBuilder colSB = new StringBuilder(); + if (valueMap != null) { + for (Map.Entry entry : valueMap.entrySet()) { + colSB.append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n"); + } + } + return colSB.toString(); + } + + private String renderMemoryUsage(Map valueMap) { + final StringBuilder colSB = new StringBuilder(); + String[] keys = new String[]{"init", "used", "committed", "max"}; + for (String key : keys) { + Object value = valueMap.get(key); + String valueStr = value != null ? formatMemoryByte((Long) value) : ""; + colSB.append(key).append(" : ").append(valueStr).append("\n"); + } + return colSB.toString(); + } + + private String formatMemoryByte(long bytes) { + return String.format("%s(%s)", bytes, StringUtils.humanReadableByteCount(bytes)); + } +}