Transform commands of pkg monitor200 (#1306)

* transform command: logger

* transform command: dashboard, thread

* transform commands: heapdump, perfcounter

* transform command: jvm, improve MEMORY section

* transform command: mbean
This commit is contained in:
hengyunabc
2020-07-16 19:57:37 +08:00
committed by GitHub
34 changed files with 2451 additions and 731 deletions
@@ -1,7 +1,5 @@
package com.taobao.arthas.core.command.logger;
import static com.taobao.text.ui.Element.label;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.Instrumentation;
@@ -21,6 +19,7 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.common.IOUtils;
import com.taobao.arthas.common.ReflectUtils;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.LoggerModel;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.ClassLoaderUtils;
@@ -29,15 +28,11 @@ import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import com.taobao.text.Decoration;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
/**
* logger command
*
* @author hengyunabc 2019-09-04
*
* @author hengyunabc 2019-09-04
*/
//@formatter:off
@Name("logger")
@@ -60,7 +55,7 @@ public class LoggerCommand extends AnnotatedCommand {
private static Map<Class<?>, byte[]> classToBytesMap = new HashMap<Class<?>, byte[]>();
private static String arthasClassLoaderHash = ClassLoaderUtils
.classLoaderHash(LoggerCommand.class.getClassLoader());
.classLoaderHash(LoggerCommand.class.getClassLoader());
static {
LoggerHelperBytes = loadClassBytes(LoggerHelper.class);
@@ -111,15 +106,11 @@ public class LoggerCommand extends AnnotatedCommand {
@Override
public void process(CommandProcess process) {
int status = 0;
try {
if (this.name != null && this.level != null) {
level(process);
} else {
loggers(process, name);
}
} finally {
process.end(status);
// 每个分支中调用process.end()结束执行
if (this.name != null && this.level != null) {
level(process);
} else {
loggers(process, name);
}
}
@@ -154,9 +145,9 @@ public class LoggerCommand extends AnnotatedCommand {
}
if (result) {
process.write("Update logger level success.\n");
process.end(0, "Update logger level success.");
} else {
process.write("Update logger level fail. Try to specify the classloader with the -c option. Use `sc -d CLASSNAME` to find out the classloader hashcode.\n");
process.end(-1, "Update logger level fail. Try to specify the classloader with the -c option. Use `sc -d CLASSNAME` to find out the classloader hashcode.");
}
}
@@ -194,97 +185,21 @@ public class LoggerCommand extends AnnotatedCommand {
if (loggerTypes.contains(LoggerType.LOG4J)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, Log4jHelper.class);
String renderResult = renderLoggerInfo(loggerInfoMap, process.width());
process.write(renderResult);
process.appendResult(new LoggerModel(loggerInfoMap));
}
if (loggerTypes.contains(LoggerType.LOGBACK)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, LogbackHelper.class);
String renderResult = renderLoggerInfo(loggerInfoMap, process.width());
process.write(renderResult);
process.appendResult(new LoggerModel(loggerInfoMap));
}
if (loggerTypes.contains(LoggerType.LOG4J2)) {
Map<String, Map<String, Object>> loggerInfoMap = loggerInfo(classLoader, Log4j2Helper.class);
String renderResult = renderLoggerInfo(loggerInfoMap, process.width());
process.write(renderResult);
process.appendResult(new LoggerModel(loggerInfoMap));
}
}
}
private String renderLoggerInfo(Map<String, Map<String, Object>> loggerInfos, int width) {
StringBuilder sb = new StringBuilder(8192);
for (Entry<String, Map<String, Object>> entry : loggerInfos.entrySet()) {
Map<String, Object> info = entry.getValue();
TableElement table = new TableElement(2, 10).leftCellPadding(1).rightCellPadding(1);
TableElement appendersTable = new TableElement().rightCellPadding(1);
Class<?> clazz = (Class<?>) info.get(LoggerHelper.clazz);
table.row(label(LoggerHelper.name).style(Decoration.bold.bold()), label("" + info.get(LoggerHelper.name)))
.row(label(LoggerHelper.clazz).style(Decoration.bold.bold()), label("" + clazz.getName()))
.row(label(LoggerHelper.classLoader).style(Decoration.bold.bold()),
label("" + clazz.getClassLoader()))
.row(label(LoggerHelper.classLoaderHash).style(Decoration.bold.bold()),
label("" + StringUtils.classLoaderHash(clazz)))
.row(label(LoggerHelper.level).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.level)));
if (info.get(LoggerHelper.effectiveLevel) != null) {
table.row(label(LoggerHelper.effectiveLevel).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.effectiveLevel)));
}
if (info.get(LoggerHelper.config) != null) {
table.row(label(LoggerHelper.config).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.config)));
}
table.row(label(LoggerHelper.additivity).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.additivity)))
.row(label(LoggerHelper.codeSource).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.codeSource)));
@SuppressWarnings("unchecked")
List<Map<String, Object>> appenders = (List<Map<String, Object>>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
for (Map<String, Object> appenderInfo : appenders) {
Class<?> appenderClass = (Class<?>) appenderInfo.get(LoggerHelper.clazz);
appendersTable.row(label(LoggerHelper.name).style(Decoration.bold.bold()),
label("" + appenderInfo.get(LoggerHelper.name)));
appendersTable.row(label(LoggerHelper.clazz), label("" + appenderClass.getName()));
appendersTable.row(label(LoggerHelper.classLoader), label("" + appenderClass.getClassLoader()));
appendersTable.row(label(LoggerHelper.classLoaderHash),
label("" + StringUtils.classLoaderHash(appenderClass)));
if (appenderInfo.get(LoggerHelper.file) != null) {
appendersTable.row(label(LoggerHelper.file), label("" + appenderInfo.get(LoggerHelper.file)));
}
if (appenderInfo.get(LoggerHelper.target) != null) {
appendersTable.row(label(LoggerHelper.target),
label("" + appenderInfo.get(LoggerHelper.target)));
}
if (appenderInfo.get(LoggerHelper.blocking) != null) {
appendersTable.row(label(LoggerHelper.blocking),
label("" + appenderInfo.get(LoggerHelper.blocking)));
}
if (appenderInfo.get(LoggerHelper.appenderRef) != null) {
appendersTable.row(label(LoggerHelper.appenderRef),
label("" + appenderInfo.get(LoggerHelper.appenderRef)));
}
}
table.row(label("appenders").style(Decoration.bold.bold()), appendersTable);
}
sb.append(RenderUtil.render(table, width)).append('\n');
}
return sb.toString();
process.end();
}
private static String helperClassNameWithClassLoader(ClassLoader classLoader, Class<?> helperClass) {
@@ -304,25 +219,46 @@ public class LoggerCommand extends AnnotatedCommand {
} catch (ClassNotFoundException e) {
try {
byte[] helperClassBytes = AsmRenameUtil.renameClass(classToBytesMap.get(helperClass),
helperClass.getName(), helperClassName);
helperClass.getName(), helperClassName);
ReflectUtils.defineClass(helperClassName, helperClassBytes, classLoader);
} catch (Throwable e1) {
logger.error("arthas loggger command try to define helper class error: " + helperClassName,
e1);
e1);
}
}
try {
Class<?> clazz = classLoader.loadClass(helperClassName);
Method getLoggersMethod = clazz.getMethod("getLoggers", new Class<?>[] { String.class, boolean.class });
Method getLoggersMethod = clazz.getMethod("getLoggers", new Class<?>[]{String.class, boolean.class});
loggers = (Map<String, Map<String, Object>>) getLoggersMethod.invoke(null,
new Object[] { name, includeNoAppender });
new Object[]{name, includeNoAppender});
} catch (Throwable e) {
// ignore
}
//expose attributes to json: classloader, classloaderHash
for (Map<String, Object> loggerInfo : loggers.values()) {
Class clazz = (Class) loggerInfo.get(LoggerHelper.clazz);
loggerInfo.put(LoggerHelper.classLoader, getClassLoaderName(clazz.getClassLoader()));
loggerInfo.put(LoggerHelper.classLoaderHash, StringUtils.classLoaderHash(clazz));
List<Map<String, Object>> appenders = (List<Map<String, Object>>) loggerInfo.get(LoggerHelper.appenders);
for (Map<String, Object> appenderInfo : appenders) {
Class appenderClass = (Class) appenderInfo.get(LoggerHelper.clazz);
if (appenderClass != null) {
appenderInfo.put(LoggerHelper.classLoader, getClassLoaderName(appenderClass.getClassLoader()));
appenderInfo.put(LoggerHelper.classLoaderHash, StringUtils.classLoaderHash(appenderClass));
}
}
}
return loggers;
}
private String getClassLoaderName(ClassLoader classLoader) {
return classLoader == null ? null : classLoader.toString();
}
private Boolean updateLevel(Instrumentation inst, Class<?> helperClass) throws Exception {
ClassLoader classLoader = null;
if (hashCode == null) {
@@ -332,8 +268,8 @@ public class LoggerCommand extends AnnotatedCommand {
}
Class<?> clazz = classLoader.loadClass(helperClassNameWithClassLoader(classLoader, helperClass));
Method updateLevelMethod = clazz.getMethod("updateLevel", new Class<?>[] { String.class, String.class });
return (Boolean) updateLevelMethod.invoke(null, new Object[] { this.name, this.level });
Method updateLevelMethod = clazz.getMethod("updateLevel", new Class<?>[]{String.class, String.class});
return (Boolean) updateLevelMethod.invoke(null, new Object[]{this.name, this.level});
}
@@ -360,7 +296,7 @@ public class LoggerCommand extends AnnotatedCommand {
private static byte[] loadClassBytes(Class<?> clazz) {
try {
InputStream stream = LoggerCommand.class.getClassLoader()
.getResourceAsStream(clazz.getName().replace('.', '/') + ".class");
.getResourceAsStream(clazz.getName().replace('.', '/') + ".class");
return IOUtils.getBytes(stream);
} catch (IOException e) {
@@ -0,0 +1,45 @@
package com.taobao.arthas.core.command.model;
import java.lang.management.ThreadInfo;
/**
* Thread blocking lock info, extract from ThreadUtil.
*
* @author gongdewei 2020/7/14
*/
public class BlockingLockInfo {
// the thread info that is holing this lock.
private ThreadInfo threadInfo = null;
// the associated LockInfo object
private int lockIdentityHashCode = 0;
// the number of thread that is blocked on this lock
private int blockingThreadCount = 0;
public BlockingLockInfo() {
}
public ThreadInfo getThreadInfo() {
return threadInfo;
}
public void setThreadInfo(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
public int getLockIdentityHashCode() {
return lockIdentityHashCode;
}
public void setLockIdentityHashCode(int lockIdentityHashCode) {
this.lockIdentityHashCode = lockIdentityHashCode;
}
public int getBlockingThreadCount() {
return blockingThreadCount;
}
public void setBlockingThreadCount(int blockingThreadCount) {
this.blockingThreadCount = blockingThreadCount;
}
}
@@ -0,0 +1,61 @@
package com.taobao.arthas.core.command.model;
import java.util.List;
import java.util.Map;
/**
* Model of 'dashboard' command
* @author gongdewei 2020/4/22
*/
public class DashboardModel extends ResultModel {
private List<ThreadVO> threads;
private Map<String, List<MemoryEntryVO>> memoryInfo;
private List<GcInfoVO> gcInfos;
private RuntimeInfoVO runtimeInfo;
private TomcatInfoVO tomcatInfo;
@Override
public String getType() {
return "dashboard";
}
public List<ThreadVO> getThreads() {
return threads;
}
public void setThreads(List<ThreadVO> threads) {
this.threads = threads;
}
public Map<String, List<MemoryEntryVO>> getMemoryInfo() {
return memoryInfo;
}
public void setMemoryInfo(Map<String, List<MemoryEntryVO>> memoryInfo) {
this.memoryInfo = memoryInfo;
}
public List<GcInfoVO> getGcInfos() {
return gcInfos;
}
public void setGcInfos(List<GcInfoVO> gcInfos) {
this.gcInfos = gcInfos;
}
public RuntimeInfoVO getRuntimeInfo() {
return runtimeInfo;
}
public void setRuntimeInfo(RuntimeInfoVO runtimeInfo) {
this.runtimeInfo = runtimeInfo;
}
public TomcatInfoVO getTomcatInfo() {
return tomcatInfo;
}
public void setTomcatInfo(TomcatInfoVO tomcatInfo) {
this.tomcatInfo = tomcatInfo;
}
}
@@ -0,0 +1,41 @@
package com.taobao.arthas.core.command.model;
/**
* GC info of dashboard
* @author gongdewei 2020/4/23
*/
public class GcInfoVO {
private String name;
private long collectionCount;
private long collectionTime;
public GcInfoVO(String name, long collectionCount, long collectionTime) {
this.name = name;
this.collectionCount = collectionCount;
this.collectionTime = collectionTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getCollectionCount() {
return collectionCount;
}
public void setCollectionCount(long collectionCount) {
this.collectionCount = collectionCount;
}
public long getCollectionTime() {
return collectionTime;
}
public void setCollectionTime(long collectionTime) {
this.collectionTime = collectionTime;
}
}
@@ -0,0 +1,42 @@
package com.taobao.arthas.core.command.model;
/**
* Model of `heapdump` command
* @author gongdewei 2020/4/24
*/
public class HeapDumpModel extends ResultModel {
private String dumpFile;
private boolean live;
public HeapDumpModel() {
}
public HeapDumpModel(String dumpFile, boolean live) {
this.dumpFile = dumpFile;
this.live = live;
}
public String getDumpFile() {
return dumpFile;
}
public void setDumpFile(String dumpFile) {
this.dumpFile = dumpFile;
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
@Override
public String getType() {
return "heapdump";
}
}
@@ -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;
}
}
@@ -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<String, List<JvmItemVO>> jvmInfo;
public JvmModel() {
jvmInfo = Collections.synchronizedMap(new LinkedHashMap<String, List<JvmItemVO>>());
}
@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<JvmItemVO> group(String group) {
synchronized (this) {
List<JvmItemVO> list = jvmInfo.get(group);
if (list == null) {
list = new ArrayList<JvmItemVO>();
jvmInfo.put(group, list);
}
return list;
}
}
public Map<String, List<JvmItemVO>> getJvmInfo() {
return jvmInfo;
}
}
@@ -0,0 +1,34 @@
package com.taobao.arthas.core.command.model;
import java.util.Map;
/**
* Model of logger command
*
* @author gongdewei 2020/4/22
*/
public class LoggerModel extends ResultModel {
private Map<String, Map<String, Object>> loggerInfoMap;
public LoggerModel() {
}
public LoggerModel(Map<String, Map<String, Object>> loggerInfoMap) {
this.loggerInfoMap = loggerInfoMap;
}
public Map<String, Map<String, Object>> getLoggerInfoMap() {
return loggerInfoMap;
}
public void setLoggerInfoMap(Map<String, Map<String, Object>> loggerInfoMap) {
this.loggerInfoMap = loggerInfoMap;
}
@Override
public String getType() {
return "logger";
}
}
@@ -0,0 +1,47 @@
package com.taobao.arthas.core.command.model;
/**
* MBean attribute
*
* @author gongdewei 2020/4/26
*/
public class MBeanAttributeVO {
private String name;
private Object value;
private String error;
public MBeanAttributeVO(String name, Object value) {
this.name = name;
this.value = value;
}
public MBeanAttributeVO(String name, Object value, String error) {
this.name = name;
this.value = value;
this.error = error;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
@@ -0,0 +1,55 @@
package com.taobao.arthas.core.command.model;
import javax.management.MBeanInfo;
import java.util.List;
import java.util.Map;
/**
* Model of 'mbean'
*
* @author gongdewei 2020/4/26
*/
public class MBeanModel extends ResultModel {
private List<String> mbeanNames;
private Map<String, MBeanInfo> mbeanMetadata;
private Map<String, List<MBeanAttributeVO>> mbeanAttribute;
public MBeanModel() {
}
public MBeanModel(List<String> mbeanNames) {
this.mbeanNames = mbeanNames;
}
@Override
public String getType() {
return "mbean";
}
public List<String> getMbeanNames() {
return mbeanNames;
}
public void setMbeanNames(List<String> mbeanNames) {
this.mbeanNames = mbeanNames;
}
public Map<String, MBeanInfo> getMbeanMetadata() {
return mbeanMetadata;
}
public void setMbeanMetadata(Map<String, MBeanInfo> mbeanMetadata) {
this.mbeanMetadata = mbeanMetadata;
}
public Map<String, List<MBeanAttributeVO>> getMbeanAttribute() {
return mbeanAttribute;
}
public void setMbeanAttribute(Map<String, List<MBeanAttributeVO>> mbeanAttribute) {
this.mbeanAttribute = mbeanAttribute;
}
}
@@ -0,0 +1,70 @@
package com.taobao.arthas.core.command.model;
/**
* Memory info of 'dashboard' command
*
* @author gongdewei 2020/4/22
*/
public class MemoryEntryVO {
public static final String TYPE_HEAP = "heap";
public static final String TYPE_NON_HEAP = "nonheap";
public static final String TYPE_BUFFER_POOL = "buffer_pool";
private String type;
private String name;
private long used;
private long total;
private long max;
public MemoryEntryVO() {
}
public MemoryEntryVO(String type, String name, long used, long total, long max) {
this.type = type;
this.name = name;
this.used = used;
this.total = total;
this.max = max;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getUsed() {
return used;
}
public void setUsed(long used) {
this.used = used;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public long getMax() {
return max;
}
public void setMax(long max) {
this.max = max;
}
}
@@ -0,0 +1,42 @@
package com.taobao.arthas.core.command.model;
import java.util.List;
/**
* Model of 'perfcounter'
*
* @author gongdewei 2020/4/27
*/
public class PerfCounterModel extends ResultModel {
private List<PerfCounterVO> perfCounters;
private boolean details;
public PerfCounterModel() {
}
public PerfCounterModel(List<PerfCounterVO> perfCounters, boolean details) {
this.perfCounters = perfCounters;
this.details = details;
}
@Override
public String getType() {
return "perfcounter";
}
public List<PerfCounterVO> getPerfCounters() {
return perfCounters;
}
public void setPerfCounters(List<PerfCounterVO> perfCounters) {
this.perfCounters = perfCounters;
}
public boolean isDetails() {
return details;
}
public void setDetails(boolean details) {
this.details = details;
}
}
@@ -0,0 +1,62 @@
package com.taobao.arthas.core.command.model;
/**
* VO for PerfCounterCommand
*
* @author gongdewei 2020/4/27
*/
public class PerfCounterVO {
private String name;
private String units;
private String variability;
private Object value;
public PerfCounterVO() {
}
public PerfCounterVO(String name, Object value) {
this.name = name;
this.value = value;
}
public PerfCounterVO(String name, String units, String variability, Object value) {
this.name = name;
this.units = units;
this.variability = variability;
this.value = value;
}
public void setName(String name) {
this.name = name;
}
public void setUnits(String units) {
this.units = units;
}
public void setVariability(String variability) {
this.variability = variability;
}
public void setValue(Object value) {
this.value = value;
}
public String getName() {
return name;
}
public String getUnits() {
return units;
}
public String getVariability() {
return variability;
}
public Object getValue() {
return value;
}
}
@@ -0,0 +1,75 @@
package com.taobao.arthas.core.command.model;
/**
* Dashboard - Runtime
*
* @author gongdewei 2020/4/22
*/
public class RuntimeInfoVO {
private String osName;
private String osVersion;
private String javaVersion;
private String javaHome;
private double systemLoadAverage;
private int processors;
private long uptime;
public RuntimeInfoVO() {
}
public String getOsName() {
return osName;
}
public void setOsName(String osName) {
this.osName = osName;
}
public String getOsVersion() {
return osVersion;
}
public void setOsVersion(String osVersion) {
this.osVersion = osVersion;
}
public String getJavaVersion() {
return javaVersion;
}
public void setJavaVersion(String javaVersion) {
this.javaVersion = javaVersion;
}
public String getJavaHome() {
return javaHome;
}
public void setJavaHome(String javaHome) {
this.javaHome = javaHome;
}
public double getSystemLoadAverage() {
return systemLoadAverage;
}
public void setSystemLoadAverage(double systemLoadAverage) {
this.systemLoadAverage = systemLoadAverage;
}
public int getProcessors() {
return processors;
}
public void setProcessors(int processors) {
this.processors = processors;
}
public long getUptime() {
return uptime;
}
public void setUptime(long uptime) {
this.uptime = uptime;
}
}
@@ -0,0 +1,36 @@
package com.taobao.arthas.core.command.model;
import java.lang.management.ThreadInfo;
/**
* ThreadInfo with cpuUsage
*
* @author gongdewei 2020/4/26
*/
public class ThreadCpuInfo {
private ThreadInfo threadInfo;
private long cpuUsage;
public ThreadCpuInfo(ThreadInfo threadInfo, long cpuUsage) {
this.threadInfo = threadInfo;
this.cpuUsage = cpuUsage;
}
public long getCpuUsage() {
return cpuUsage;
}
public void setCpuUsage(long cpuUsage) {
this.cpuUsage = cpuUsage;
}
public ThreadInfo threadInfo() {
return threadInfo;
}
public void setThreadInfo(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
}
@@ -0,0 +1,91 @@
package com.taobao.arthas.core.command.model;
import java.lang.management.ThreadInfo;
import java.util.List;
import java.util.Map;
/**
* Model of 'thread' command
*
* @author gongdewei 2020/4/26
*/
public class ThreadModel extends ResultModel {
//single thread: thread 12
private ThreadInfo threadInfo;
//thread -b
private BlockingLockInfo blockingLockInfo;
//thread -n 5
private ThreadCpuInfo[] busyThreads;
//thread stats
private List<ThreadVO> threadStats;
private Map<Thread.State, Integer> threadStateCount;
public ThreadModel() {
}
public ThreadModel(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
public ThreadModel(BlockingLockInfo blockingLockInfo) {
this.blockingLockInfo = blockingLockInfo;
}
public ThreadModel(ThreadCpuInfo[] busyThreads) {
this.busyThreads = busyThreads;
}
public ThreadModel(List<ThreadVO> threadStats, Map<Thread.State, Integer> threadStateCount) {
this.threadStats = threadStats;
this.threadStateCount = threadStateCount;
}
@Override
public String getType() {
return "thread";
}
public ThreadInfo getThreadInfo() {
return threadInfo;
}
public void setThreadInfo(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
public BlockingLockInfo getBlockingLockInfo() {
return blockingLockInfo;
}
public void setBlockingLockInfo(BlockingLockInfo blockingLockInfo) {
this.blockingLockInfo = blockingLockInfo;
}
public ThreadCpuInfo[] getBusyThreads() {
return busyThreads;
}
public void setBusyThreads(ThreadCpuInfo[] busyThreads) {
this.busyThreads = busyThreads;
}
public List<ThreadVO> getThreadStats() {
return threadStats;
}
public void setThreadStats(List<ThreadVO> threadStats) {
this.threadStats = threadStats;
}
public Map<Thread.State, Integer> getThreadStateCount() {
return threadStateCount;
}
public void setThreadStateCount(Map<Thread.State, Integer> threadStateCount) {
this.threadStateCount = threadStateCount;
}
}
@@ -0,0 +1,95 @@
package com.taobao.arthas.core.command.model;
import java.lang.Thread.State;
/**
* Thread VO of 'dashboard' and 'thread' command
*
* @author gongdewei 2020/4/22
*/
public class ThreadVO {
private long id;
private String name;
private String group;
private int priority;
private State state;
private long cpu;
private long time;
private boolean interrupted;
private boolean daemon;
public ThreadVO() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public long getCpu() {
return cpu;
}
public void setCpu(long cpu) {
this.cpu = cpu;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public boolean isInterrupted() {
return interrupted;
}
public void setInterrupted(boolean interrupted) {
this.interrupted = interrupted;
}
public boolean isDaemon() {
return daemon;
}
public void setDaemon(boolean daemon) {
this.daemon = daemon;
}
}
@@ -0,0 +1,129 @@
package com.taobao.arthas.core.command.model;
import java.util.List;
/**
* Tomcat info of 'dashboard' command
*
* @author gongdewei 2020/4/23
*/
public class TomcatInfoVO {
private List<ConnectorStats> connectorStats;
private List<ThreadPool> threadPools;
public TomcatInfoVO() {
}
public List<ConnectorStats> getConnectorStats() {
return connectorStats;
}
public void setConnectorStats(List<ConnectorStats> connectorStats) {
this.connectorStats = connectorStats;
}
public List<ThreadPool> getThreadPools() {
return threadPools;
}
public void setThreadPools(List<ThreadPool> threadPools) {
this.threadPools = threadPools;
}
public static class ConnectorStats {
private String name;
private double qps;
private double rt;
private double error;
private long received;
private long sent;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getQps() {
return qps;
}
public void setQps(double qps) {
this.qps = qps;
}
public double getRt() {
return rt;
}
public void setRt(double rt) {
this.rt = rt;
}
public double getError() {
return error;
}
public void setError(double error) {
this.error = error;
}
public long getReceived() {
return received;
}
public void setReceived(long received) {
this.received = received;
}
public long getSent() {
return sent;
}
public void setSent(long sent) {
this.sent = sent;
}
}
public static class ThreadPool {
private String name;
private long busy;
private long total;
public ThreadPool() {
}
public ThreadPool(String name, long busy, long total) {
this.name = name;
this.busy = busy;
this.total = total;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getBusy() {
return busy;
}
public void setBusy(long busy) {
this.busy = busy;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
}
}
@@ -5,6 +5,11 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.DashboardModel;
import com.taobao.arthas.core.command.model.GcInfoVO;
import com.taobao.arthas.core.command.model.MemoryEntryVO;
import com.taobao.arthas.core.command.model.RuntimeInfoVO;
import com.taobao.arthas.core.command.model.TomcatInfoVO;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.handlers.Handler;
@@ -18,13 +23,6 @@ import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import com.taobao.text.Color;
import com.taobao.text.Decoration;
import com.taobao.text.Style;
import com.taobao.text.renderers.ThreadRenderer;
import com.taobao.text.ui.RowElement;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import java.lang.management.BufferPoolMXBean;
import java.lang.management.GarbageCollectorMXBean;
@@ -32,11 +30,17 @@ import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_BUFFER_POOL;
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_HEAP;
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_NON_HEAP;
/**
* @author hengyunabc 2015年11月19日 上午11:57:21
*/
@@ -140,8 +144,43 @@ public class DashboardCommand extends AnnotatedCommand {
return name.replace(' ', '_').toLowerCase();
}
private static void addBufferPoolMemoryInfo(TableElement table) {
private static void addMemoryInfo(DashboardModel dashboardModel) {
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
Map<String, List<MemoryEntryVO>> memoryInfoMap = new LinkedHashMap<String, List<MemoryEntryVO>>();
dashboardModel.setMemoryInfo(memoryInfoMap);
//heap
MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
List<MemoryEntryVO> heapMemEntries = new ArrayList<MemoryEntryVO>();
heapMemEntries.add(createMemoryEntryVO(TYPE_HEAP, TYPE_HEAP, heapMemoryUsage));
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
if (MemoryType.HEAP.equals(poolMXBean.getType())) {
MemoryUsage usage = poolMXBean.getUsage();
String poolName = beautifyName(poolMXBean.getName());
heapMemEntries.add(createMemoryEntryVO(TYPE_HEAP, poolName, usage));
}
}
memoryInfoMap.put(TYPE_HEAP, heapMemEntries);
//non-heap
MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
List<MemoryEntryVO> nonheapMemEntries = new ArrayList<MemoryEntryVO>();
nonheapMemEntries.add(createMemoryEntryVO(TYPE_NON_HEAP, TYPE_NON_HEAP, nonHeapMemoryUsage));
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
if (MemoryType.NON_HEAP.equals(poolMXBean.getType())) {
MemoryUsage usage = poolMXBean.getUsage();
String poolName = beautifyName(poolMXBean.getName());
nonheapMemEntries.add(createMemoryEntryVO(TYPE_NON_HEAP, poolName, usage));
}
}
memoryInfoMap.put(TYPE_NON_HEAP, nonheapMemEntries);
addBufferPoolMemoryInfo(memoryInfoMap);
}
private static void addBufferPoolMemoryInfo(Map<String, List<MemoryEntryVO>> memoryInfoMap) {
try {
List<MemoryEntryVO> bufferPoolMemEntries = new ArrayList<MemoryEntryVO>();
@SuppressWarnings("rawtypes")
Class bufferPoolMXBeanClass = Class.forName("java.lang.management.BufferPoolMXBean");
@SuppressWarnings("unchecked")
@@ -149,84 +188,57 @@ public class DashboardCommand extends AnnotatedCommand {
for (BufferPoolMXBean mbean : bufferPoolMXBeans) {
long used = mbean.getMemoryUsed();
long total = mbean.getTotalCapacity();
new MemoryEntry(mbean.getName(), used, total, Long.MIN_VALUE).addTableRow(table);
bufferPoolMemEntries.add(new MemoryEntryVO(TYPE_BUFFER_POOL, mbean.getName(), used, total, Long.MIN_VALUE));
}
memoryInfoMap.put(TYPE_BUFFER_POOL, bufferPoolMemEntries);
} catch (ClassNotFoundException e) {
// ignore
}
}
private static void addRuntimeInfo(TableElement table) {
table.row("os.name", System.getProperty("os.name"));
table.row("os.version", System.getProperty("os.version"));
table.row("java.version", System.getProperty("java.version"));
table.row("java.home", System.getProperty("java.home"));
table.row("systemload.average",
String.format("%.2f", ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage()));
table.row("processors", "" + Runtime.getRuntime().availableProcessors());
table.row("uptime", "" + ManagementFactory.getRuntimeMXBean().getUptime() / 1000 + "s");
private static void addRuntimeInfo(DashboardModel dashboardModel) {
RuntimeInfoVO runtimeInfo = new RuntimeInfoVO();
runtimeInfo.setOsName(System.getProperty("os.name"));
runtimeInfo.setOsVersion(System.getProperty("os.version"));
runtimeInfo.setJavaVersion(System.getProperty("java.version"));
runtimeInfo.setJavaHome(System.getProperty("java.home"));
runtimeInfo.setSystemLoadAverage(ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage());
runtimeInfo.setProcessors(Runtime.getRuntime().availableProcessors());
runtimeInfo.setUptime(ManagementFactory.getRuntimeMXBean().getUptime() / 1000);
dashboardModel.setRuntimeInfo(runtimeInfo);
}
private static void addMemoryInfo(TableElement table) {
MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
new MemoryEntry("heap", heapMemoryUsage).addTableRow(table, Decoration.bold.bold());
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
if (MemoryType.HEAP.equals(poolMXBean.getType())) {
MemoryUsage usage = poolMXBean.getUsage();
String poolName = beautifyName(poolMXBean.getName());
new MemoryEntry(poolName, usage).addTableRow(table);
}
}
new MemoryEntry("nonheap", nonHeapMemoryUsage).addTableRow(table, Decoration.bold.bold());
for (MemoryPoolMXBean poolMXBean : memoryPoolMXBeans) {
if (MemoryType.NON_HEAP.equals(poolMXBean.getType())) {
MemoryUsage usage = poolMXBean.getUsage();
String poolName = beautifyName(poolMXBean.getName());
new MemoryEntry(poolName, usage).addTableRow(table);
}
}
addBufferPoolMemoryInfo(table);
private static MemoryEntryVO createMemoryEntryVO(String type, String name, MemoryUsage memoryUsage) {
return new MemoryEntryVO(type, name, memoryUsage.getUsed(), memoryUsage.getCommitted(), memoryUsage.getMax());
}
private static void addGcInfo(TableElement table) {
private static void addGcInfo(DashboardModel dashboardModel) {
List<GcInfoVO> gcInfos = new ArrayList<GcInfoVO>();
dashboardModel.setGcInfos(gcInfos);
List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) {
String name = garbageCollectorMXBean.getName();
table.add(new RowElement().style(Decoration.bold.bold()).add("gc." + beautifyName(name) + ".count",
"" + garbageCollectorMXBean.getCollectionCount()));
table.row("gc." + beautifyName(name) + ".time(ms)", "" + garbageCollectorMXBean.getCollectionTime());
for (GarbageCollectorMXBean gcMXBean : garbageCollectorMxBeans) {
String name = gcMXBean.getName();
gcInfos.add(new GcInfoVO(beautifyName(name), gcMXBean.getCollectionCount(), gcMXBean.getCollectionTime()));
}
}
private static String formatBytes(long size) {
int unit = 1;
String unitStr = "B";
if (size / 1024 > 0) {
unit = 1024;
unitStr = "K";
} else if (size / 1024 / 1024 > 0) {
unit = 1024 * 1024;
unitStr = "M";
private void addTomcatInfo(DashboardModel dashboardModel) {
// 如果请求tomcat信息失败,则不显示tomcat信息
if (!NetUtils.request("http://localhost:8006").isSuccess()) {
return;
}
return String.format("%d%s", size / unit, unitStr);
}
private void addTomcatInfo(TableElement table) {
TomcatInfoVO tomcatInfoVO = new TomcatInfoVO();
dashboardModel.setTomcatInfo(tomcatInfoVO);
String threadPoolPath = "http://localhost:8006/connector/threadpool";
String connectorStatPath = "http://localhost:8006/connector/stats";
Response connectorStatResponse = NetUtils.request(connectorStatPath);
if (connectorStatResponse.isSuccess()) {
List<JSONObject> connectorStats = JSON.parseArray(connectorStatResponse.getContent(), JSONObject.class);
for (JSONObject stat : connectorStats) {
String name = stat.getString("name").replace("\"", "");
List<TomcatInfoVO.ConnectorStats> connectorStats = new ArrayList<TomcatInfoVO.ConnectorStats>();
List<JSONObject> tomcatConnectorStats = JSON.parseArray(connectorStatResponse.getContent(), JSONObject.class);
for (JSONObject stat : tomcatConnectorStats) {
String connectorName = stat.getString("name").replace("\"", "");
long bytesReceived = stat.getLongValue("bytesReceived");
long bytesSent = stat.getLongValue("bytesSent");
long processingTime = stat.getLongValue("processingTime");
@@ -238,173 +250,86 @@ public class DashboardCommand extends AnnotatedCommand {
tomcatReceivedBytesCounter.update(bytesReceived);
tomcatSentBytesCounter.update(bytesSent);
table.add(new RowElement().style(Decoration.bold.bold()).add("connector", name));
table.row("QPS", String.format("%.2f", tomcatRequestCounter.rate()));
table.row("RT(ms)", String.format("%.2f", processingTime / (double) requestCount));
table.row("error/s", String.format("%.2f", tomcatErrorCounter.rate()));
table.row("received/s", formatBytes((long) tomcatReceivedBytesCounter.rate()));
table.row("sent/s", formatBytes((long) tomcatSentBytesCounter.rate()));
double qps = tomcatRequestCounter.rate();
double rt = processingTime / (double) requestCount;
double errorRate = tomcatErrorCounter.rate();
long receivedBytesRate = new Double(tomcatReceivedBytesCounter.rate()).longValue();
long sentBytesRate = new Double(tomcatSentBytesCounter.rate()).longValue();
TomcatInfoVO.ConnectorStats connectorStat = new TomcatInfoVO.ConnectorStats();
connectorStat.setName(connectorName);
connectorStat.setQps(qps);
connectorStat.setRt(rt);
connectorStat.setError(errorRate);
connectorStat.setReceived(receivedBytesRate);
connectorStat.setSent(sentBytesRate);
connectorStats.add(connectorStat);
}
tomcatInfoVO.setConnectorStats(connectorStats);
}
Response threadPoolResponse = NetUtils.request(threadPoolPath);
if (threadPoolResponse.isSuccess()) {
List<TomcatInfoVO.ThreadPool> threadPools = new ArrayList<TomcatInfoVO.ThreadPool>();
List<JSONObject> threadPoolInfos = JSON.parseArray(threadPoolResponse.getContent(), JSONObject.class);
for (JSONObject info : threadPoolInfos) {
String name = info.getString("name").replace("\"", "");
long busy = info.getLongValue("threadBusy");
long total = info.getLongValue("threadCount");
table.add(new RowElement().style(Decoration.bold.bold()).add("threadpool", name));
table.row("busy", "" + busy);
table.row("total", "" + total);
threadPools.add(new TomcatInfoVO.ThreadPool(name, busy, total));
}
}
}
static String drawThreadInfo(int width, int height) {
Map<String, Thread> threads = ThreadUtil.getThreads();
return RenderUtil.render(threads.values().iterator(), new ThreadRenderer(), width, height);
}
static String drawMemoryInfoAndGcInfo(int width, int height) {
TableElement table = new TableElement(1, 1);
TableElement memoryInfoTable = new TableElement(3, 1, 1, 1, 1).rightCellPadding(1);
memoryInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Memory",
"used", "total", "max", "usage"));
addMemoryInfo(memoryInfoTable);
TableElement gcInfoTable = new TableElement(1, 1).rightCellPadding(1);
gcInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("GC", ""));
addGcInfo(gcInfoTable);
table.row(memoryInfoTable, gcInfoTable);
return RenderUtil.render(table, width, height);
}
String drawRuntimeInfoAndTomcatInfo(int width, int height) {
TableElement resultTable = new TableElement(1, 1);
TableElement runtimeInfoTable = new TableElement(1, 1).rightCellPadding(1);
runtimeInfoTable
.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Runtime", ""));
addRuntimeInfo(runtimeInfoTable);
TableElement tomcatInfoTable = null;
try {
// 如果请求tomcat信息失败,则不显示tomcat信息
if (NetUtils.request("http://localhost:8006").isSuccess()) {
tomcatInfoTable = new TableElement(1, 1).rightCellPadding(1);
tomcatInfoTable
.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Tomcat", ""));
addTomcatInfo(tomcatInfoTable);
}
} catch (Throwable t) {
logger.error("get Tomcat Info error!", t);
}
if (tomcatInfoTable != null) {
resultTable.row(runtimeInfoTable, tomcatInfoTable);
} else {
resultTable = runtimeInfoTable;
}
return RenderUtil.render(resultTable, width, height);
}
static class MemoryEntry {
String name;
long used;
long total;
long max;
int unit;
String unitStr;
public MemoryEntry(String name, long used, long total, long max) {
this.name = name;
this.used = used;
this.total = total;
this.max = max;
unitStr = "K";
unit = 1024;
if (used / 1024 / 1024 > 0) {
unitStr = "M";
unit = 1024 * 1024;
}
}
public MemoryEntry(String name, MemoryUsage usage) {
this(name, usage.getUsed(), usage.getCommitted(), usage.getMax());
}
private String format(long value) {
String valueStr = "-";
if (value == -1) {
return "-1";
}
if (value != Long.MIN_VALUE) {
valueStr = value / unit + unitStr;
}
return valueStr;
}
public void addTableRow(TableElement table) {
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
table.row(name, format(used), format(total), format(max), String.format("%.2f%%", usage));
}
public void addTableRow(TableElement table, Style.Composite style) {
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
table.add(new RowElement().style(style).add(name, format(used), format(total), format(max),
String.format("%.2f%%", usage)));
tomcatInfoVO.setThreadPools(threadPools);
}
}
private class DashboardTimerTask extends TimerTask {
private CommandProcess process;
private ThreadSampler threadSampler;
public DashboardTimerTask(CommandProcess process) {
this.process = process;
this.threadSampler = new ThreadSampler();
}
@Override
public void run() {
if (count >= getNumOfExecutions()) {
// stop the timer
timer.cancel();
timer.purge();
process.write("Process ends after " + getNumOfExecutions() + " time(s).\n");
process.end();
return;
try {
if (count >= getNumOfExecutions()) {
// stop the timer
timer.cancel();
timer.purge();
process.end(0, "Process ends after " + getNumOfExecutions() + " time(s).");
return;
}
DashboardModel dashboardModel = new DashboardModel();
//thread sample
Map<String, Thread> threads = ThreadUtil.getThreads();
dashboardModel.setThreads(threadSampler.sample(threads.values()));
//memory
addMemoryInfo(dashboardModel);
//gc
addGcInfo(dashboardModel);
//runtime
addRuntimeInfo(dashboardModel);
//tomcat
addTomcatInfo(dashboardModel);
process.appendResult(dashboardModel);
count++;
process.times().incrementAndGet();
} catch (Throwable e) {
String msg = "process dashboard failed: " + e.getMessage();
logger.error(msg, e);
process.end(-1, msg);
}
int width = process.width();
int height = process.height();
// 上半部分放thread top。下半部分再切分为田字格,其中上面两格放memory, gc的信息。下面两格放tomcat,
// runtime的信息
int totalHeight = height - 1;
int threadTopHeight = totalHeight / 2;
int lowerHalf = totalHeight - threadTopHeight;
int runtimeInfoHeight = lowerHalf / 2;
int heapInfoHeight = lowerHalf - runtimeInfoHeight;
String threadInfo = drawThreadInfo(width, threadTopHeight);
String memoryAndGc = drawMemoryInfoAndGcInfo(width, runtimeInfoHeight);
String runTimeAndTomcat = drawRuntimeInfoAndTomcatInfo(width, heapInfoHeight);
process.write(threadInfo + memoryAndGc + runTimeAndTomcat);
count++;
process.times().incrementAndGet();
}
}
}
@@ -10,6 +10,8 @@ import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.sun.management.HotSpotDiagnosticMXBean;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.HeapDumpModel;
import com.taobao.arthas.core.command.model.MessageModel;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.middleware.cli.annotations.Argument;
@@ -48,7 +50,6 @@ public class HeapDumpCommand extends AnnotatedCommand {
@Override
public void process(CommandProcess process) {
int status = 0;
try {
String dumpFile = file;
if (dumpFile == null || dumpFile.isEmpty()) {
@@ -58,18 +59,17 @@ public class HeapDumpCommand extends AnnotatedCommand {
file.delete();
}
process.write("Dumping heap to " + dumpFile + "...\n");
process.appendResult(new MessageModel("Dumping heap to " + dumpFile + " ..."));
run(process, dumpFile, live);
process.write("Heap dump file created\n");
process.appendResult(new MessageModel("Heap dump file created"));
process.appendResult(new HeapDumpModel(dumpFile, live));
process.end();
} catch (Throwable t) {
logger.error("heap dump error", t);
process.write("Heap dump error: " + t.getMessage() + '\n');
status = 1;
} finally {
process.end(status);
String errorMsg = "heap dump error: " + t.getMessage();
logger.error(errorMsg, t);
process.end(-1, errorMsg);
}
}
@@ -1,25 +1,20 @@
package com.taobao.arthas.core.command.monitor200;
import com.taobao.arthas.core.command.Constants;
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 +38,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 +82,111 @@ public class JvmCommand extends AnnotatedCommand {
return -1;
}
}
private String toCol(Collection<String> 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<String, Object> gcInfo = new LinkedHashMap<String, Object>();
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<String, Object> 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())
);
Map<String, Object> nonheapMemoryInfo = getMemoryUsageInfo("nonheap", nonHeapMemoryUsage);
jvmModel.addItem(group,"NO-HEAP-MEMORY-USAGE", nonheapMemoryInfo, "memory in bytes");
table.row("PENDING-FINALIZE-COUNT", "" + memoryMXBean.getObjectPendingFinalizationCount());
return table;
}
private String formatMemoryByte(long bytes){
return String.format("%s(%s)",bytes, StringUtils.humanReadableByteCount(bytes));
jvmModel.addItem(group,"PENDING-FINALIZE-COUNT", memoryMXBean.getObjectPendingFinalizationCount());
}
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 Map<String, Object> getMemoryUsageInfo(String name, MemoryUsage heapMemoryUsage) {
Map<String, Object> memoryInfo = new LinkedHashMap<String, Object>();
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 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 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 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) {
@@ -3,6 +3,8 @@ package com.taobao.arthas.core.command.monitor200;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.MBeanAttributeVO;
import com.taobao.arthas.core.command.model.MBeanModel;
import com.taobao.arthas.core.shell.cli.CliToken;
import com.taobao.arthas.core.shell.cli.Completion;
import com.taobao.arthas.core.shell.cli.CompletionUtils;
@@ -22,38 +24,25 @@ import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import com.taobao.text.Color;
import com.taobao.text.Decoration;
import com.taobao.text.ui.LabelElement;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javax.management.Descriptor;
import javax.management.DescriptorRead;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import static com.taobao.text.ui.Element.label;
import static javax.management.MBeanOperationInfo.ACTION;
import static javax.management.MBeanOperationInfo.ACTION_INFO;
import static javax.management.MBeanOperationInfo.INFO;
import static javax.management.MBeanOperationInfo.UNKNOWN;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
/**
* Date: 2019/4/18
@@ -63,13 +52,16 @@ import static javax.management.MBeanOperationInfo.UNKNOWN;
@Name("mbean")
@Summary("Display the mbean information")
@Description("\nExamples:\n" +
" mbean\n" +
" mbean -m java.lang:type=Threading\n" +
" mbean java.lang:type=Threading\n" +
" mbean java.lang:type=Threading *Count\n" +
" mbean -E java.lang:type=Threading PeakThreadCount|ThreadCount|DaemonThreadCount\n" +
" mbean -i 1000 java.lang:type=Threading *Count\n" +
Constants.WIKI + Constants.WIKI_HOME + "mbean")
" mbean\n" +
" mbean -m java.lang:type=Threading\n" +
" mbean java.lang:type=Threading\n" +
" mbean java.lang:type=Threading *Count\n" +
" mbean java.lang:type=MemoryPool,name=PS\\ Old\\ Gen\n" +
" mbean java.lang:type=MemoryPool,name=*\n" +
" mbean java.lang:type=MemoryPool,name=* Usage\n" +
" mbean -E java.lang:type=Threading PeakThreadCount|ThreadCount|DaemonThreadCount\n" +
" mbean -i 1000 java.lang:type=Threading *Count\n" +
Constants.WIKI + Constants.WIKI_HOME + "mbean")
public class MBeanCommand extends AnnotatedCommand {
private static final Logger logger = LoggerFactory.getLogger(MBeanCommand.class);
@@ -84,7 +76,12 @@ public class MBeanCommand extends AnnotatedCommand {
private long count = 0;
@Argument(argName = "name-pattern", index = 0, required = false)
@Description("ObjectName pattern, see javax.management.ObjectName for more detail.")
@Description("ObjectName pattern, see javax.management.ObjectName for more detail. \n" +
"It looks like this: \n" +
" domain: key-property-list\n" +
"For example: \n" +
" java.lang:name=G1 Old Gen,type=MemoryPool\n" +
" java.lang:name=*,type=MemoryPool")
public void setNamePattern(String name) {
this.name = name;
}
@@ -107,7 +104,7 @@ public class MBeanCommand extends AnnotatedCommand {
isRegEx = regEx;
}
@Option(shortName = "m", longName = "metadata", flag = false)
@Option(shortName = "m", longName = "metadata", flag = true)
@Description("Show metadata of mbean.")
public void setMetaData(boolean metaData) {
this.metaData = metaData;
@@ -141,6 +138,7 @@ public class MBeanCommand extends AnnotatedCommand {
@Override
public void process(CommandProcess process) {
//每个分支调用process.end()结束执行
if (StringUtils.isEmpty(getName())) {
listMBean(process);
} else if (isMetaData()) {
@@ -152,10 +150,11 @@ public class MBeanCommand extends AnnotatedCommand {
private void listMBean(CommandProcess process) {
Set<ObjectName> objectNames = queryObjectNames();
List<String> mbeanNames = new ArrayList<String>(objectNames.size());
for (ObjectName objectName : objectNames) {
process.write(objectName.toString());
process.write("\n");
mbeanNames.add(objectName.toString());
}
process.appendResult(new MBeanModel(mbeanNames));
process.end();
}
@@ -192,6 +191,8 @@ public class MBeanCommand extends AnnotatedCommand {
} else {
timer.schedule(new MBeanTimerTask(process), 0);
}
//异步执行,这里不能调用process.end(),在timer task中结束命令执行
}
public synchronized void stop() {
@@ -214,19 +215,18 @@ public class MBeanCommand extends AnnotatedCommand {
Set<ObjectName> objectNames = queryObjectNames();
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
try {
TableElement table = createTable();
MBeanModel mbeanModel = new MBeanModel();
Map<String, MBeanInfo> mbeanMetaData = new LinkedHashMap<String, MBeanInfo>();
mbeanModel.setMbeanMetadata(mbeanMetaData);
for (ObjectName objectName : objectNames) {
MBeanInfo mBeanInfo = mBeanServer.getMBeanInfo(objectName);
drawMetaInfo(mBeanInfo, objectName, table);
drawAttributeInfo(mBeanInfo.getAttributes(), table);
drawOperationInfo(mBeanInfo.getOperations(), table);
drawNotificationInfo(mBeanInfo.getNotifications(), table);
mbeanMetaData.put(objectName.toString(), mBeanInfo);
}
process.write(RenderUtil.render(table, process.width()));
process.appendResult(mbeanModel);
process.end();
} catch (Throwable e) {
logger.warn("listMetaData error", e);
} finally {
process.end();
process.end(1, "list mbean metadata error");
}
}
@@ -348,102 +348,6 @@ public class MBeanCommand extends AnnotatedCommand {
return isRegEx ? new RegexMatcher(attribute) : new WildcardMatcher(attribute);
}
private void drawMetaInfo(MBeanInfo mBeanInfo, ObjectName objectName, TableElement table) {
table.row(new LabelElement("MBeanInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Info:").style(Decoration.bold.fg(Color.yellow)));
table.row("ObjectName", objectName.toString());
table.row("ClassName", mBeanInfo.getClassName());
table.row("Description", mBeanInfo.getDescription());
drawDescriptorInfo("Info Descriptor:", mBeanInfo, table);
MBeanConstructorInfo[] constructors = mBeanInfo.getConstructors();
if (constructors.length > 0) {
for (int i = 0; i < constructors.length; i++) {
table.row(new LabelElement("Constructor-" + i).style(Decoration.bold.fg(Color.yellow)));
table.row("Name", constructors[i].getName());
table.row("Description", constructors[i].getDescription());
}
}
}
private void drawAttributeInfo(MBeanAttributeInfo[] attributes, TableElement table) {
for (MBeanAttributeInfo attribute : attributes) {
table.row(new LabelElement("MBeanAttributeInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Attribute:").style(Decoration.bold.fg(Color.yellow)));
table.row("Name", attribute.getName());
table.row("Description", attribute.getDescription());
table.row("Readable", String.valueOf(attribute.isReadable()));
table.row("Writable", String.valueOf(attribute.isWritable()));
table.row("Is", String.valueOf(attribute.isIs()));
table.row("Type", attribute.getType());
drawDescriptorInfo("Attribute Descriptor:", attribute, table);
}
}
private void drawOperationInfo(MBeanOperationInfo[] operations, TableElement table) {
for (MBeanOperationInfo operation : operations) {
table.row(new LabelElement("MBeanOperationInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Operation:").style(Decoration.bold.fg(Color.yellow)));
table.row("Name", operation.getName());
table.row("Description", operation.getDescription());
String impact = "";
switch (operation.getImpact()) {
case ACTION:
impact = "action";
break;
case ACTION_INFO:
impact = "action/info";
break;
case INFO:
impact = "info";
break;
case UNKNOWN:
impact = "unknown";
break;
}
table.row("Impact", impact);
table.row("ReturnType", operation.getReturnType());
MBeanParameterInfo[] signature = operation.getSignature();
if (signature.length > 0) {
for (int i = 0; i < signature.length; i++) {
table.row(new LabelElement("Parameter-" + i).style(Decoration.bold.fg(Color.yellow)));
table.row("Name", signature[i].getName());
table.row("Type", signature[i].getType());
table.row("Description", signature[i].getDescription());
}
}
drawDescriptorInfo("Operation Descriptor:", operation, table);
}
}
private void drawNotificationInfo(MBeanNotificationInfo[] notificationInfos, TableElement table) {
for (MBeanNotificationInfo notificationInfo : notificationInfos) {
table.row(new LabelElement("MBeanNotificationInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Notification:").style(Decoration.bold.fg(Color.yellow)));
table.row("Name", notificationInfo.getName());
table.row("Description", notificationInfo.getDescription());
table.row("NotifTypes", Arrays.toString(notificationInfo.getNotifTypes()));
drawDescriptorInfo("Notification Descriptor:", notificationInfo, table);
}
}
private void drawDescriptorInfo(String title, DescriptorRead descriptorRead, TableElement table) {
Descriptor descriptor = descriptorRead.getDescriptor();
String[] fieldNames = descriptor.getFieldNames();
if (fieldNames.length > 0) {
table.row(new LabelElement(title).style(Decoration.bold.fg(Color.yellow)));
for (String fieldName : fieldNames) {
Object fieldValue = descriptor.getFieldValue(fieldName);
table.row(fieldName, fieldValue.toString());
}
}
}
private static TableElement createTable() {
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
table.row(true, label("NAME").style(Decoration.bold.bold()),
label("VALUE").style(Decoration.bold.bold()));
return table;
}
public class MBeanInterruptHandler extends CommandInterruptHandler {
@@ -475,16 +379,20 @@ public class MBeanCommand extends AnnotatedCommand {
// stop the timer
timer.cancel();
timer.purge();
process.write("Process ends after " + getNumOfExecutions() + " time(s).\n");
process.end();
process.end(-1, "Process ends after " + getNumOfExecutions() + " time(s).");
return;
}
try {
//result model
MBeanModel mBeanModel = new MBeanModel();
Map<String, List<MBeanAttributeVO>> mbeanAttributeMap = new LinkedHashMap<String, List<MBeanAttributeVO>>();
mBeanModel.setMbeanAttribute(mbeanAttributeMap);
MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> objectNames = queryObjectNames();
TableElement table = createTable();
for (ObjectName objectName : objectNames) {
List<MBeanAttributeVO> attributeVOs = null;
MBeanInfo mBeanInfo = platformMBeanServer.getMBeanInfo(objectName);
MBeanAttributeInfo[] attributes = mBeanInfo.getAttributes();
for (MBeanAttributeInfo attribute : attributes) {
@@ -492,29 +400,99 @@ public class MBeanCommand extends AnnotatedCommand {
if (!getAttributeMatcher().matching(attributeName)) {
continue;
}
String value;
//create attributeVO list
if (attributeVOs == null) {
attributeVOs = new ArrayList<MBeanAttributeVO>();
mbeanAttributeMap.put(objectName.toString(), attributeVOs);
}
if (!attribute.isReadable()) {
value = RenderUtil.render(new LabelElement("Unavailable").style(Decoration.bold_off.fg(Color.red)));
attributeVOs.add(new MBeanAttributeVO(attributeName, null, "Unavailable"));
} else {
Object attributeObj = platformMBeanServer.getAttribute(objectName, attributeName);
value = String.valueOf(attributeObj);
try {
Object attributeObj = platformMBeanServer.getAttribute(objectName, attributeName);
attributeVOs.add(createMBeanAttributeVO(attributeName, attributeObj));
} catch (Throwable e) {
logger.error("read mbean attribute failed: objectName={}, attributeName={}", objectName, attributeName, e);
String errorStr;
Throwable cause = e.getCause();
if (cause != null && cause instanceof UnsupportedOperationException) {
errorStr = "Unsupported";
} else {
errorStr = "Failure";
}
attributeVOs.add(new MBeanAttributeVO(attributeName, null, errorStr));
}
}
table.row(attributeName, value);
}
process.write(RenderUtil.render(table, process.width()));
process.write("\n");
}
process.appendResult(mBeanModel);
} catch (Throwable e) {
logger.warn("mbean error", e);
logger.warn("read mbean error", e);
stop();
process.end(1, "read mbean error.");
return;
}
count++;
process.times().incrementAndGet();
if (getInterval() <= 0) {
stop();
process.end();
}
}
}
private MBeanAttributeVO createMBeanAttributeVO(String attributeName, Object originAttrValue) {
Object attrValue = convertAttrValue(attributeName, originAttrValue);
return new MBeanAttributeVO(attributeName, attrValue);
}
private Object convertAttrValue(String attributeName, Object originAttrValue) {
Object attrValue = originAttrValue;
try {
if (originAttrValue instanceof ObjectName) {
attrValue = String.valueOf(originAttrValue);
} else if (attrValue instanceof CompositeData) {
//mbean java.lang:type=MemoryPool,name=*
CompositeData compositeData = (CompositeData) attrValue;
attrValue = convertCompositeData(attributeName, compositeData);
} else if (attrValue instanceof CompositeData[]) {
//mbean com.sun.management:type=HotSpotDiagnostic
CompositeData[] compositeDataArray = (CompositeData[]) attrValue;
List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>(compositeDataArray.length);
for (CompositeData compositeData : compositeDataArray) {
dataList.add(convertCompositeData(attributeName, compositeData));
}
attrValue = dataList;
} else if (attrValue instanceof TabularData) {
//mbean java.lang:type=GarbageCollector,name=*
TabularData tabularData = (TabularData) attrValue;
Collection<CompositeData> compositeDataList = (Collection<CompositeData>) tabularData.values();
List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>(compositeDataList.size());
for (CompositeData compositeData : compositeDataList) {
dataList.add(convertCompositeData(attributeName, compositeData));
}
attrValue = dataList;
}
} catch (Throwable e) {
logger.error("convert mbean attribute error, attribute: {}={}", attributeName, originAttrValue, e);
attrValue = String.valueOf(originAttrValue);
}
return attrValue;
}
private Map<String, Object> convertCompositeData(String attributeName, CompositeData compositeData) {
Set<String> keySet = compositeData.getCompositeType().keySet();
String[] keys = keySet.toArray(new String[0]);
Object[] values = compositeData.getAll(keys);
Map<String, Object> data = new LinkedHashMap<String, Object>();
for (int i = 0; i < keys.length; i++) {
data.put(keys[i], convertAttrValue(attributeName + "." + keys[i], values[i]));
}
return data;
}
}
@@ -1,9 +1,9 @@
package com.taobao.arthas.core.command.monitor200;
import static com.taobao.text.ui.Element.label;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -12,15 +12,14 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.taobao.arthas.common.JavaVersionUtils;
import com.taobao.arthas.common.PidUtils;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.PerfCounterModel;
import com.taobao.arthas.core.command.model.PerfCounterVO;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import com.taobao.text.Decoration;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import sun.management.counter.Counter;
import sun.management.counter.perf.PerfInstrumentation;
@@ -51,36 +50,26 @@ public class PerfCounterCommand extends AnnotatedCommand {
@Override
public void process(CommandProcess process) {
try {
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
if (this.details) {
table = new TableElement(3, 1, 1, 10).leftCellPadding(1).rightCellPadding(1);
table.row(true, label("Name").style(Decoration.bold.bold()),
label("Variability").style(Decoration.bold.bold()),
label("Units").style(Decoration.bold.bold()), label("Value").style(Decoration.bold.bold()));
}
List<Counter> perfCounters = getPerfCounters();
if (perfCounters.isEmpty()) {
process.write(
"please check arthas log. if java version >=9 , try to add jvm options when start your process: "
+ "--add-opens java.base/jdk.internal.perf=ALL-UNNAMED "
+ "--add-exports java.base/jdk.internal.perf=ALL-UNNAMED\n");
} else {
for (Counter counter : perfCounters) {
if (details) {
table.row(counter.getName(), String.valueOf(counter.getVariability()),
String.valueOf(counter.getUnits()), String.valueOf(counter.getValue()));
} else {
table.row(counter.getName(), String.valueOf(counter.getValue()));
}
}
}
process.write(RenderUtil.render(table, process.width()));
} finally {
process.end();
List<Counter> perfCounters = getPerfCounters();
if (perfCounters.isEmpty()) {
process.end(1,
"please check arthas log. if java version >=9 , try to add jvm options when start your process: "
+ "--add-opens java.base/jdk.internal.perf=ALL-UNNAMED "
+ "--add-exports java.base/jdk.internal.perf=ALL-UNNAMED");
return;
}
List<PerfCounterVO> perfCounterVOs = new ArrayList<PerfCounterVO>();
for (Counter counter : perfCounters) {
PerfCounterVO perfCounterVO = new PerfCounterVO(counter.getName(), counter.getValue());
if (details) {
perfCounterVO.setUnits(counter.getUnits().toString());
perfCounterVO.setVariability(counter.getVariability().toString());
}
perfCounterVOs.add(perfCounterVO);
}
process.appendResult(new PerfCounterModel(perfCounterVOs, details));
process.end();
}
private static List<Counter> getPerfCounters() {
@@ -1,21 +1,22 @@
package com.taobao.arthas.core.command.monitor200;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.BlockingLockInfo;
import com.taobao.arthas.core.command.model.ThreadCpuInfo;
import com.taobao.arthas.core.command.model.ThreadModel;
import com.taobao.arthas.core.command.model.ThreadVO;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.command.ExitStatus;
import com.taobao.arthas.core.util.ArrayUtils;
import com.taobao.arthas.core.util.CommandUtils;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.arthas.core.util.ThreadUtil;
import com.taobao.arthas.core.util.affect.Affect;
import com.taobao.arthas.core.util.affect.RowAffect;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import com.taobao.text.renderers.ThreadRenderer;
import com.taobao.text.ui.LabelElement;
import com.taobao.text.util.RenderUtil;
import java.lang.Thread.State;
import java.lang.management.ManagementFactory;
@@ -23,8 +24,9 @@ import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -106,31 +108,24 @@ public class ThreadCommand extends AnnotatedCommand {
@Override
public void process(CommandProcess process) {
Affect affect = new RowAffect();
int status = 0;
try {
if (id > 0) {
status = processThread(process);
} else if (topNBusy != null) {
status = processTopBusyThreads(process);
} else if (findMostBlockingThread) {
status = processBlockingThread(process);
} else {
status = processAllThreads(process);
}
} finally {
process.write(affect + "\n");
process.end(status);
ExitStatus exitStatus;
if (id > 0) {
exitStatus = processThread(process);
} else if (topNBusy != null) {
exitStatus = processTopBusyThreads(process);
} else if (findMostBlockingThread) {
exitStatus = processBlockingThread(process);
} else {
exitStatus = processAllThreads(process);
}
CommandUtils.end(process, exitStatus);
}
private int processAllThreads(CommandProcess process) {
int status = 0;
private ExitStatus processAllThreads(CommandProcess process) {
Map<String, Thread> threads = ThreadUtil.getThreads();
// 统计各种线程状态
StringBuilder threadStat = new StringBuilder();
Map<State, Integer> stateCountMap = new HashMap<State, Integer>();
Map<State, Integer> stateCountMap = new LinkedHashMap<State, Integer>();
for (State s : State.values()) {
stateCountMap.put(s, 0);
}
@@ -141,81 +136,65 @@ public class ThreadCommand extends AnnotatedCommand {
stateCountMap.put(threadState, count + 1);
}
threadStat.append("Threads Total: ").append(threads.values().size());
for (State s : State.values()) {
Integer count = stateCountMap.get(s);
threadStat.append(", ").append(s.name()).append(": ").append(count);
}
String stat = RenderUtil.render(new LabelElement(threadStat), process.width());
Collection<Thread> resultThreads = new ArrayList<Thread>();
if (!StringUtils.isEmpty(this.state)){
if (!StringUtils.isEmpty(this.state)) {
this.state = this.state.toUpperCase();
if(states.contains(this.state)) {
if (states.contains(this.state)) {
for (Thread thread : threads.values()) {
if (state.equals(thread.getState().name())) {
resultThreads.add(thread);
}
}
}else{
process.write("Illegal argument, state should be one of " + states + "\n");
status = 1;
return status;
} else {
return ExitStatus.failure(1, "Illegal argument, state should be one of " + states);
}
} else {
resultThreads = threads.values();
}
String content = RenderUtil.render(resultThreads.iterator(),
new ThreadRenderer(sampleInterval), process.width());
process.write(stat + content);
return status;
//thread stats
ThreadSampler threadSampler = new ThreadSampler();
threadSampler.setSampleInterval(sampleInterval);
List<ThreadVO> threadStats = threadSampler.sample(resultThreads);
process.appendResult(new ThreadModel(threadStats, stateCountMap));
return ExitStatus.success();
}
private int processBlockingThread(CommandProcess process) {
int status = 0;
ThreadUtil.BlockingLockInfo blockingLockInfo = ThreadUtil.findMostBlockingLock();
if (blockingLockInfo.threadInfo == null) {
process.write("No most blocking thread found!\n");
status = 1;
} else {
String stacktrace = ThreadUtil.getFullStacktrace(blockingLockInfo);
process.write(stacktrace);
private ExitStatus processBlockingThread(CommandProcess process) {
BlockingLockInfo blockingLockInfo = ThreadUtil.findMostBlockingLock();
if (blockingLockInfo.getThreadInfo() == null) {
return ExitStatus.failure(1, "No most blocking thread found!");
}
return status;
process.appendResult(new ThreadModel(blockingLockInfo));
return ExitStatus.success();
}
private int processTopBusyThreads(CommandProcess process) {
int status = 0;
private ExitStatus processTopBusyThreads(CommandProcess process) {
Map<Long, Long> topNThreads = ThreadUtil.getTopNThreads(sampleInterval, topNBusy);
Long[] tids = topNThreads.keySet().toArray(new Long[0]);
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(ArrayUtils.toPrimitive(tids), lockedMonitors, lockedSynchronizers);
if (threadInfos == null) {
process.write("thread do not exist! id: " + id + "\n");
status = 1;
} else {
for (ThreadInfo info : threadInfos) {
String stacktrace = ThreadUtil.getFullStacktrace(info, topNThreads.get(info.getThreadId()));
process.write(stacktrace + "\n");
}
return ExitStatus.failure(1, "get top busy threads failed");
}
return status;
//threadInfo with cpuUsage
ThreadCpuInfo[] threadCpuInfos = new ThreadCpuInfo[threadInfos.length];
for (int i = 0; i < threadInfos.length; i++) {
ThreadInfo threadInfo = threadInfos[i];
threadCpuInfos[i] = new ThreadCpuInfo(threadInfo, topNThreads.get(threadInfo.getThreadId()));
}
process.appendResult(new ThreadModel(threadCpuInfos));
return ExitStatus.success();
}
private int processThread(CommandProcess process) {
int status = 0;
String content;
private ExitStatus processThread(CommandProcess process) {
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(new long[]{id}, lockedMonitors, lockedSynchronizers);
if (threadInfos == null || threadInfos[0] == null) {
content = "thread do not exist! id: " + id + "\n";
status = 1;
} else {
// no cpu usage info
content = ThreadUtil.getFullStacktrace(threadInfos[0], -1);
if (threadInfos == null || threadInfos.length < 1 || threadInfos[0] == null) {
return ExitStatus.failure(1, "thread do not exist! id: " + id);
}
process.write(content);
return status;
process.appendResult(new ThreadModel(threadInfos[0]));
return ExitStatus.success();
}
}
@@ -0,0 +1,111 @@
package com.taobao.arthas.core.command.monitor200;
import com.taobao.arthas.core.command.model.ThreadVO;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.*;
/**
* Thread cpu sampler
*
* @author gongdewei 2020/4/23
*/
public class ThreadSampler {
private long sampleInterval = 100;
public List<ThreadVO> sample(Collection<Thread> originThreads) {
List<Thread> threads = new ArrayList<Thread>(originThreads);
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// Sample CPU
Map<Long, Long> times1 = new HashMap<Long, Long>();
for (Thread thread : threads) {
long cpu = threadMXBean.getThreadCpuTime(thread.getId());
times1.put(thread.getId(), cpu);
}
try {
Thread.sleep(sampleInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Resample
Map<Long, Long> times2 = new HashMap<Long, Long>(threads.size());
for (Thread thread : threads) {
long cpu = threadMXBean.getThreadCpuTime(thread.getId());
times2.put(thread.getId(), cpu);
}
// Compute delta map and total time
long total = 0;
Map<Long, Long> deltas = new HashMap<Long, Long>(threads.size());
for (Long id : times2.keySet()) {
long time1 = times2.get(id);
long time2 = times1.get(id);
if (time1 == -1) {
time1 = time2;
} else if (time2 == -1) {
time2 = time1;
}
long delta = time2 - time1;
deltas.put(id, delta);
total += delta;
}
// Compute cpu
final HashMap<Thread, Long> cpus = new HashMap<Thread, Long>(threads.size());
for (Thread thread : threads) {
long cpu = total == 0 ? 0 : Math.round((deltas.get(thread.getId()) * 100) / total);
cpus.put(thread, cpu);
}
// Sort by CPU time : should be a rendering hint...
Collections.sort(threads, new Comparator<Thread>() {
public int compare(Thread o1, Thread o2) {
long l1 = cpus.get(o1);
long l2 = cpus.get(o2);
if (l1 < l2) {
return 1;
} else if (l1 > l2) {
return -1;
} else {
return 0;
}
}
});
List<ThreadVO> threadVOList = new ArrayList<ThreadVO>(threads.size());
for (Thread thread : threads) {
ThreadGroup group = thread.getThreadGroup();
long seconds = times2.get(thread.getId()) / 1000000000;
//long min = seconds / 60;
//String time = min + ":" + (seconds % 60);
long cpu = cpus.get(thread);
ThreadVO threadVO = new ThreadVO();
threadVO.setId(thread.getId());
threadVO.setName(thread.getName());
threadVO.setGroup(group == null ? "" : group.getName());
threadVO.setPriority(thread.getPriority());
threadVO.setState(thread.getState());
threadVO.setCpu(cpu);
threadVO.setTime(seconds);
threadVO.setInterrupted(thread.isInterrupted());
threadVO.setDaemon(thread.isDaemon());
threadVOList.add(threadVO);
}
return threadVOList;
}
public long getSampleInterval() {
return sampleInterval;
}
public void setSampleInterval(long sampleInterval) {
this.sampleInterval = sampleInterval;
}
}
@@ -0,0 +1,225 @@
package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.model.*;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.text.Color;
import com.taobao.text.Decoration;
import com.taobao.text.Style;
import com.taobao.text.ui.RowElement;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import java.lang.management.MemoryUsage;
import java.util.List;
import java.util.Map;
/**
* View of 'dashboard' command
*
* @author gongdewei 2020/4/22
*/
public class DashboardView extends ResultView<DashboardModel> {
@Override
public void draw(CommandProcess process, DashboardModel result) {
int width = process.width();
int height = process.height();
// 上半部分放thread top。下半部分再切分为田字格,其中上面两格放memory, gc的信息。下面两格放tomcat,
// runtime的信息
int totalHeight = height - 1;
int threadTopHeight = totalHeight / 2;
int lowerHalf = totalHeight - threadTopHeight;
int runtimeInfoHeight = lowerHalf / 2;
int heapInfoHeight = lowerHalf - runtimeInfoHeight;
String threadInfo = ViewRenderUtil.drawThreadInfo(result.getThreads(), width, threadTopHeight);
String memoryAndGc = drawMemoryInfoAndGcInfo(result.getMemoryInfo(), result.getGcInfos(), width, runtimeInfoHeight);
String runTimeAndTomcat = drawRuntimeInfoAndTomcatInfo(result.getRuntimeInfo(), result.getTomcatInfo(), width, heapInfoHeight);
process.write(threadInfo + memoryAndGc + runTimeAndTomcat);
}
static String drawMemoryInfoAndGcInfo(Map<String, List<MemoryEntryVO>> memoryInfo, List<GcInfoVO> gcInfos, int width, int height) {
TableElement table = new TableElement(1, 1);
TableElement memoryInfoTable = new TableElement(3, 1, 1, 1, 1).rightCellPadding(1);
memoryInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Memory",
"used", "total", "max", "usage"));
drawMemoryInfo(memoryInfoTable, memoryInfo);
TableElement gcInfoTable = new TableElement(1, 1).rightCellPadding(1);
gcInfoTable.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("GC", ""));
drawGcInfo(gcInfoTable, gcInfos);
table.row(memoryInfoTable, gcInfoTable);
return RenderUtil.render(table, width, height);
}
private static void drawMemoryInfo(TableElement table, Map<String, List<MemoryEntryVO>> memoryInfo) {
List<MemoryEntryVO> heapMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_HEAP);
//heap memory
for (MemoryEntryVO memoryEntryVO : heapMemoryEntries) {
if (MemoryEntryVO.TYPE_HEAP.equals(memoryEntryVO.getName())) {
new MemoryEntry(memoryEntryVO).addTableRow(table, Decoration.bold.bold());
} else {
new MemoryEntry(memoryEntryVO).addTableRow(table);
}
}
//non-heap memory
List<MemoryEntryVO> nonheapMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_NON_HEAP);
for (MemoryEntryVO memoryEntryVO : nonheapMemoryEntries) {
if (MemoryEntryVO.TYPE_NON_HEAP.equals(memoryEntryVO.getName())) {
new MemoryEntry(memoryEntryVO).addTableRow(table, Decoration.bold.bold());
} else {
new MemoryEntry(memoryEntryVO).addTableRow(table);
}
}
//buffer-pool
List<MemoryEntryVO> bufferPoolMemoryEntries = memoryInfo.get(MemoryEntryVO.TYPE_BUFFER_POOL);
for (MemoryEntryVO memoryEntryVO : bufferPoolMemoryEntries) {
new MemoryEntry(memoryEntryVO).addTableRow(table);
}
}
private static void drawGcInfo(TableElement table, List<GcInfoVO> gcInfos) {
for (GcInfoVO gcInfo : gcInfos) {
table.add(new RowElement().style(Decoration.bold.bold()).add("gc." + gcInfo.getName() + ".count",
"" + gcInfo.getCollectionCount()));
table.row("gc." + gcInfo.getName() + ".time(ms)", "" + gcInfo.getCollectionTime());
}
}
String drawRuntimeInfoAndTomcatInfo(RuntimeInfoVO runtimeInfo, TomcatInfoVO tomcatInfo, int width, int height) {
TableElement resultTable = new TableElement(1, 1);
//runtime
TableElement runtimeInfoTable = drawRuntimeInfo(runtimeInfo);
//tomcat
TableElement tomcatInfoTable = drawTomcatInfo(tomcatInfo);
if (tomcatInfoTable != null) {
resultTable.row(runtimeInfoTable, tomcatInfoTable);
} else {
resultTable = runtimeInfoTable;
}
return RenderUtil.render(resultTable, width, height);
}
private static TableElement drawRuntimeInfo(RuntimeInfoVO runtimeInfo) {
TableElement table = new TableElement(1, 1).rightCellPadding(1);
table.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Runtime", ""));
table.row("os.name", runtimeInfo.getOsName());
table.row("os.version", runtimeInfo.getOsVersion());
table.row("java.version", runtimeInfo.getJavaVersion());
table.row("java.home", runtimeInfo.getJavaHome());
table.row("systemload.average", String.format("%.2f", runtimeInfo.getSystemLoadAverage()));
table.row("processors", "" + runtimeInfo.getProcessors());
table.row("uptime", "" + runtimeInfo.getUptime() + "s");
return table;
}
private static String formatBytes(long size) {
int unit = 1;
String unitStr = "B";
if (size / 1024 > 0) {
unit = 1024;
unitStr = "K";
} else if (size / 1024 / 1024 > 0) {
unit = 1024 * 1024;
unitStr = "M";
}
return String.format("%d%s", size / unit, unitStr);
}
private TableElement drawTomcatInfo(TomcatInfoVO tomcatInfo) {
if (tomcatInfo == null) {
return null;
}
//header
TableElement table = new TableElement(1, 1).rightCellPadding(1);
table.add(new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add("Tomcat", ""));
if (tomcatInfo.getConnectorStats() != null) {
for (TomcatInfoVO.ConnectorStats connectorStat : tomcatInfo.getConnectorStats()) {
table.add(new RowElement().style(Decoration.bold.bold()).add("connector", connectorStat.getName()));
table.row("QPS", String.format("%.2f", connectorStat.getQps()));
table.row("RT(ms)", String.format("%.2f", connectorStat.getRt()));
table.row("error/s", String.format("%.2f", connectorStat.getError()));
table.row("received/s", formatBytes(connectorStat.getReceived()));
table.row("sent/s", formatBytes(connectorStat.getSent()));
}
}
if (tomcatInfo.getThreadPools() != null) {
for (TomcatInfoVO.ThreadPool threadPool : tomcatInfo.getThreadPools()) {
table.add(new RowElement().style(Decoration.bold.bold()).add("threadpool", threadPool.getName()));
table.row("busy", "" + threadPool.getBusy());
table.row("total", "" + threadPool.getTotal());
}
}
return table;
}
static class MemoryEntry {
String name;
long used;
long total;
long max;
int unit;
String unitStr;
public MemoryEntry(String name, long used, long total, long max) {
this.name = name;
this.used = used;
this.total = total;
this.max = max;
unitStr = "K";
unit = 1024;
if (used / 1024 / 1024 > 0) {
unitStr = "M";
unit = 1024 * 1024;
}
}
public MemoryEntry(String name, MemoryUsage usage) {
this(name, usage.getUsed(), usage.getCommitted(), usage.getMax());
}
public MemoryEntry(MemoryEntryVO memoryEntryVO) {
this(memoryEntryVO.getName(), memoryEntryVO.getUsed(), memoryEntryVO.getTotal(), memoryEntryVO.getMax());
}
private String format(long value) {
String valueStr = "-";
if (value == -1) {
return "-1";
}
if (value != Long.MIN_VALUE) {
valueStr = value / unit + unitStr;
}
return valueStr;
}
public void addTableRow(TableElement table) {
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
table.row(name, format(used), format(total), format(max), String.format("%.2f%%", usage));
}
public void addTableRow(TableElement table, Style.Composite style) {
double usage = used / (double) (max == -1 || max == Long.MIN_VALUE ? total : max) * 100;
table.add(new RowElement().style(style).add(name, format(used), format(total), format(max),
String.format("%.2f%%", usage)));
}
}
}
@@ -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<JvmModel> {
@Override
public void draw(CommandProcess process, JvmModel result) {
TableElement table = new TableElement(2, 5).leftCellPadding(1).rightCellPadding(1);
for (Map.Entry<String, List<JvmItemVO>> entry : result.getJvmInfo().entrySet()) {
String group = entry.getKey();
List<JvmItemVO> 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<String, Object>) 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<String> 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<String, Object> valueMap) {
final StringBuilder colSB = new StringBuilder();
if (valueMap != null) {
for (Map.Entry<String, Object> entry : valueMap.entrySet()) {
colSB.append(entry.getKey()).append(" : ").append(entry.getValue()).append("\n");
}
}
return colSB.toString();
}
private String renderMemoryUsage(Map<String, Object> 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));
}
}
@@ -0,0 +1,98 @@
package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.logger.LoggerHelper;
import com.taobao.arthas.core.command.model.LoggerModel;
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.List;
import java.util.Map;
import static com.taobao.text.ui.Element.label;
/**
* View of 'logger' command
*
* @author gongdewei 2020/4/22
*/
public class LoggerView extends ResultView<LoggerModel> {
@Override
public void draw(CommandProcess process, LoggerModel result) {
process.write(renderLoggerInfo(result.getLoggerInfoMap(), process.width()));
}
private String renderLoggerInfo(Map<String, Map<String, Object>> loggerInfos, int width) {
StringBuilder sb = new StringBuilder(8192);
for (Map.Entry<String, Map<String, Object>> entry : loggerInfos.entrySet()) {
Map<String, Object> info = entry.getValue();
TableElement table = new TableElement(2, 10).leftCellPadding(1).rightCellPadding(1);
TableElement appendersTable = new TableElement().rightCellPadding(1);
Class<?> clazz = (Class<?>) info.get(LoggerHelper.clazz);
table.row(label(LoggerHelper.name).style(Decoration.bold.bold()), label("" + info.get(LoggerHelper.name)))
.row(label(LoggerHelper.clazz).style(Decoration.bold.bold()), label("" + clazz.getName()))
.row(label(LoggerHelper.classLoader).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.classLoader)))
.row(label(LoggerHelper.classLoaderHash).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.classLoaderHash)))
.row(label(LoggerHelper.level).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.level)));
if (info.get(LoggerHelper.effectiveLevel) != null) {
table.row(label(LoggerHelper.effectiveLevel).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.effectiveLevel)));
}
if (info.get(LoggerHelper.config) != null) {
table.row(label(LoggerHelper.config).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.config)));
}
table.row(label(LoggerHelper.additivity).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.additivity)))
.row(label(LoggerHelper.codeSource).style(Decoration.bold.bold()),
label("" + info.get(LoggerHelper.codeSource)));
@SuppressWarnings("unchecked")
List<Map<String, Object>> appenders = (List<Map<String, Object>>) info.get(LoggerHelper.appenders);
if (appenders != null && !appenders.isEmpty()) {
for (Map<String, Object> appenderInfo : appenders) {
Class<?> appenderClass = (Class<?>) appenderInfo.get(LoggerHelper.clazz);
appendersTable.row(label(LoggerHelper.name).style(Decoration.bold.bold()),
label("" + appenderInfo.get(LoggerHelper.name)));
appendersTable.row(label(LoggerHelper.clazz), label("" + appenderClass.getName()));
appendersTable.row(label(LoggerHelper.classLoader), label("" + info.get(LoggerHelper.classLoader)));
appendersTable.row(label(LoggerHelper.classLoaderHash),
label("" + info.get(LoggerHelper.classLoaderHash)));
if (appenderInfo.get(LoggerHelper.file) != null) {
appendersTable.row(label(LoggerHelper.file), label("" + appenderInfo.get(LoggerHelper.file)));
}
if (appenderInfo.get(LoggerHelper.target) != null) {
appendersTable.row(label(LoggerHelper.target),
label("" + appenderInfo.get(LoggerHelper.target)));
}
if (appenderInfo.get(LoggerHelper.blocking) != null) {
appendersTable.row(label(LoggerHelper.blocking),
label("" + appenderInfo.get(LoggerHelper.blocking)));
}
if (appenderInfo.get(LoggerHelper.appenderRef) != null) {
appendersTable.row(label(LoggerHelper.appenderRef),
label("" + appenderInfo.get(LoggerHelper.appenderRef)));
}
}
table.row(label("appenders").style(Decoration.bold.bold()), appendersTable);
}
sb.append(RenderUtil.render(table, width)).append('\n');
}
return sb.toString();
}
}
@@ -0,0 +1,212 @@
package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.model.MBeanAttributeVO;
import com.taobao.arthas.core.command.model.MBeanModel;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.text.Color;
import com.taobao.text.Decoration;
import com.taobao.text.ui.LabelElement;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import javax.management.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static com.taobao.text.ui.Element.label;
import static javax.management.MBeanOperationInfo.*;
import static javax.management.MBeanOperationInfo.UNKNOWN;
/**
* View of 'mbean' command
*
* @author gongdewei 2020/4/26
*/
public class MBeanView extends ResultView<MBeanModel> {
@Override
public void draw(CommandProcess process, MBeanModel result) {
if (result.getMbeanNames() != null) {
drawMBeanNames(process, result.getMbeanNames());
} else if (result.getMbeanMetadata() != null) {
drawMBeanMetadata(process, result.getMbeanMetadata());
} else if (result.getMbeanAttribute() != null) {
drawMBeanAttributes(process, result.getMbeanAttribute());
}
}
private void drawMBeanAttributes(CommandProcess process, Map<String, List<MBeanAttributeVO>> mbeanAttributeMap) {
for (Map.Entry<String, List<MBeanAttributeVO>> entry : mbeanAttributeMap.entrySet()) {
String objectName = entry.getKey();
List<MBeanAttributeVO> attributeVOList = entry.getValue();
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
table.row(true, "OBJECT_NAME", objectName);
table.row(true, label("NAME").style(Decoration.bold.bold()),
label("VALUE").style(Decoration.bold.bold()));
for (MBeanAttributeVO attributeVO : attributeVOList) {
String attributeName = attributeVO.getName();
String valueStr;
if (attributeVO.getError() != null) {
valueStr = RenderUtil.render(new LabelElement(attributeVO.getError()).style(Decoration.bold_off.fg(Color.red)));
} else {
//convert array to list
// TODO support all array type
Object value = attributeVO.getValue();
if (value instanceof String[]) {
value = Arrays.asList((String[]) value);
} else if (value instanceof Integer[]) {
value = Arrays.asList((Integer[]) value);
} else if (value instanceof Long[]) {
value = Arrays.asList((Long[]) value);
} else if (value instanceof int[]) {
value = convertArrayToList((int[]) value);
} else if (value instanceof long[]) {
value = convertArrayToList((long[]) value);
}
//to string
valueStr = String.valueOf(value);
}
table.row(attributeName, valueStr);
}
process.write(RenderUtil.render(table, process.width()));
process.write("\n");
}
}
private List<Long> convertArrayToList(long[] longs) {
List<Long> list = new ArrayList<Long>();
for (long aLong : longs) {
list.add(aLong);
}
return list;
}
private List<Integer> convertArrayToList(int[] ints) {
List<Integer> list = new ArrayList<Integer>();
for (int anInt : ints) {
list.add(anInt);
}
return list;
}
private void drawMBeanMetadata(CommandProcess process, Map<String, MBeanInfo> mbeanMetadata) {
TableElement table = createTable();
for (Map.Entry<String, MBeanInfo> entry : mbeanMetadata.entrySet()) {
String objectName = entry.getKey();
MBeanInfo mBeanInfo = entry.getValue();
drawMetaInfo(mBeanInfo, objectName, table);
drawAttributeInfo(mBeanInfo.getAttributes(), table);
drawOperationInfo(mBeanInfo.getOperations(), table);
drawNotificationInfo(mBeanInfo.getNotifications(), table);
}
process.write(RenderUtil.render(table, process.width()));
}
private void drawMBeanNames(CommandProcess process, List<String> mbeanNames) {
for (String mbeanName : mbeanNames) {
process.write(mbeanName).write("\n");
}
}
private static TableElement createTable() {
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
table.row(true, label("NAME").style(Decoration.bold.bold()),
label("VALUE").style(Decoration.bold.bold()));
return table;
}
private void drawMetaInfo(MBeanInfo mBeanInfo, String objectName, TableElement table) {
table.row(new LabelElement("MBeanInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Info:").style(Decoration.bold.fg(Color.yellow)));
table.row("ObjectName", objectName);
table.row("ClassName", mBeanInfo.getClassName());
table.row("Description", mBeanInfo.getDescription());
drawDescriptorInfo("Info Descriptor:", mBeanInfo, table);
MBeanConstructorInfo[] constructors = mBeanInfo.getConstructors();
if (constructors.length > 0) {
for (int i = 0; i < constructors.length; i++) {
table.row(new LabelElement("Constructor-" + i).style(Decoration.bold.fg(Color.yellow)));
table.row("Name", constructors[i].getName());
table.row("Description", constructors[i].getDescription());
}
}
}
private void drawAttributeInfo(MBeanAttributeInfo[] attributes, TableElement table) {
for (MBeanAttributeInfo attribute : attributes) {
table.row(new LabelElement("MBeanAttributeInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Attribute:").style(Decoration.bold.fg(Color.yellow)));
table.row("Name", attribute.getName());
table.row("Description", attribute.getDescription());
table.row("Readable", String.valueOf(attribute.isReadable()));
table.row("Writable", String.valueOf(attribute.isWritable()));
table.row("Is", String.valueOf(attribute.isIs()));
table.row("Type", attribute.getType());
drawDescriptorInfo("Attribute Descriptor:", attribute, table);
}
}
private void drawOperationInfo(MBeanOperationInfo[] operations, TableElement table) {
for (MBeanOperationInfo operation : operations) {
table.row(new LabelElement("MBeanOperationInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Operation:").style(Decoration.bold.fg(Color.yellow)));
table.row("Name", operation.getName());
table.row("Description", operation.getDescription());
String impact = "";
switch (operation.getImpact()) {
case ACTION:
impact = "action";
break;
case ACTION_INFO:
impact = "action/info";
break;
case INFO:
impact = "info";
break;
case UNKNOWN:
impact = "unknown";
break;
}
table.row("Impact", impact);
table.row("ReturnType", operation.getReturnType());
MBeanParameterInfo[] signature = operation.getSignature();
if (signature.length > 0) {
for (int i = 0; i < signature.length; i++) {
table.row(new LabelElement("Parameter-" + i).style(Decoration.bold.fg(Color.yellow)));
table.row("Name", signature[i].getName());
table.row("Type", signature[i].getType());
table.row("Description", signature[i].getDescription());
}
}
drawDescriptorInfo("Operation Descriptor:", operation, table);
}
}
private void drawNotificationInfo(MBeanNotificationInfo[] notificationInfos, TableElement table) {
for (MBeanNotificationInfo notificationInfo : notificationInfos) {
table.row(new LabelElement("MBeanNotificationInfo").style(Decoration.bold.fg(Color.red)));
table.row(new LabelElement("Notification:").style(Decoration.bold.fg(Color.yellow)));
table.row("Name", notificationInfo.getName());
table.row("Description", notificationInfo.getDescription());
table.row("NotifTypes", Arrays.toString(notificationInfo.getNotifTypes()));
drawDescriptorInfo("Notification Descriptor:", notificationInfo, table);
}
}
private void drawDescriptorInfo(String title, DescriptorRead descriptorRead, TableElement table) {
Descriptor descriptor = descriptorRead.getDescriptor();
String[] fieldNames = descriptor.getFieldNames();
if (fieldNames.length > 0) {
table.row(new LabelElement(title).style(Decoration.bold.fg(Color.yellow)));
for (String fieldName : fieldNames) {
Object fieldValue = descriptor.getFieldValue(fieldName);
table.row(fieldName, fieldValue.toString());
}
}
}
}
@@ -0,0 +1,46 @@
package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.model.PerfCounterModel;
import com.taobao.arthas.core.command.model.PerfCounterVO;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.text.Decoration;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import java.util.List;
import static com.taobao.text.ui.Element.label;
/**
* View of 'perfcounter' command
*
* @author gongdewei 2020/4/27
*/
public class PerfCounterView extends ResultView<PerfCounterModel> {
@Override
public void draw(CommandProcess process, PerfCounterModel result) {
List<PerfCounterVO> perfCounters = result.getPerfCounters();
boolean details = result.isDetails();
TableElement table;
if (details) {
table = new TableElement(3, 1, 1, 10).leftCellPadding(1).rightCellPadding(1);
table.row(true, label("Name").style(Decoration.bold.bold()),
label("Variability").style(Decoration.bold.bold()),
label("Units").style(Decoration.bold.bold()), label("Value").style(Decoration.bold.bold()));
} else {
table = new TableElement(4, 6).leftCellPadding(1).rightCellPadding(1);
table.row(true, label("Name").style(Decoration.bold.bold()),
label("Value").style(Decoration.bold.bold()));
}
for (PerfCounterVO counter : perfCounters) {
if (details) {
table.row(counter.getName(), counter.getVariability(),
counter.getUnits(), String.valueOf(counter.getValue()));
} else {
table.row(counter.getName(), String.valueOf(counter.getValue()));
}
}
process.write(RenderUtil.render(table, process.width()));
}
}
@@ -60,8 +60,14 @@ public class ResultViewResolver {
registerView(SearchMethodView.class);
//logger
registerView(LoggerView.class);
//monitor2000
registerView(DashboardView.class);
registerView(JvmView.class);
registerView(MBeanView.class);
registerView(PerfCounterView.class);
registerView(ThreadView.class);
} catch (Throwable e) {
logger.error("register result view failed", e);
@@ -0,0 +1,64 @@
package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.model.ThreadCpuInfo;
import com.taobao.arthas.core.command.model.ThreadModel;
import com.taobao.arthas.core.command.model.ThreadVO;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.util.ThreadUtil;
import com.taobao.text.ui.LabelElement;
import com.taobao.text.util.RenderUtil;
import java.util.List;
import java.util.Map;
/**
* View of 'thread' command
*
* @author gongdewei 2020/4/26
*/
public class ThreadView extends ResultView<ThreadModel> {
@Override
public void draw(CommandProcess process, ThreadModel result) {
if (result.getThreadInfo() != null) {
// no cpu usage info
String content = ThreadUtil.getFullStacktrace(result.getThreadInfo(), -1);
process.write(content);
} else if (result.getBusyThreads() != null) {
ThreadCpuInfo[] threadInfos = result.getBusyThreads();
for (ThreadCpuInfo info : threadInfos) {
String stacktrace = ThreadUtil.getFullStacktrace(info.threadInfo(), info.getCpuUsage());
process.write(stacktrace).write("\n");
}
} else if (result.getBlockingLockInfo() != null) {
String stacktrace = ThreadUtil.getFullStacktrace(result.getBlockingLockInfo());
process.write(stacktrace);
} else if (result.getThreadStateCount() != null) {
Map<Thread.State, Integer> threadStateCount = result.getThreadStateCount();
List<ThreadVO> threadStats = result.getThreadStats();
//sum total thread count
int total = 0;
for (Integer value : threadStateCount.values()) {
total += value;
}
StringBuilder threadStat = new StringBuilder();
threadStat.append("Threads Total: ").append(total);
for (Thread.State s : Thread.State.values()) {
Integer count = threadStateCount.get(s);
threadStat.append(", ").append(s.name()).append(": ").append(count);
}
String stat = RenderUtil.render(new LabelElement(threadStat), process.width());
//thread stats
int height = Math.max(5, process.height() - 2);
//remove blank lines
height = Math.min(height, threadStats.size() + 2);
String content = ViewRenderUtil.drawThreadInfo(threadStats, process.width(), height);
process.write(stat + content);
}
}
}
@@ -2,11 +2,18 @@ package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.model.ChangeResultVO;
import com.taobao.arthas.core.command.model.EnhancerAffectVO;
import com.taobao.arthas.core.command.model.ThreadVO;
import com.taobao.arthas.core.util.StringUtils;
import com.taobao.text.Color;
import com.taobao.text.Decoration;
import com.taobao.text.Style;
import com.taobao.text.ui.LabelElement;
import com.taobao.text.ui.Overflow;
import com.taobao.text.ui.RowElement;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
@@ -19,6 +26,17 @@ import static java.lang.String.format;
*/
public class ViewRenderUtil {
/** Thread State Colors */
public static final EnumMap<Thread.State, Color> colorMapping = new EnumMap<Thread.State, Color>(Thread.State.class);
static {
colorMapping.put(Thread.State.NEW, Color.cyan);
colorMapping.put(Thread.State.RUNNABLE, Color.green);
colorMapping.put(Thread.State.BLOCKED, Color.red);
colorMapping.put(Thread.State.WAITING, Color.yellow);
colorMapping.put(Thread.State.TIMED_WAITING, Color.magenta);
colorMapping.put(Thread.State.TERMINATED, Color.blue);
}
/**
* Render key-value table
* @param map
@@ -85,4 +103,48 @@ public class ViewRenderUtil {
return infoSB.toString();
}
public static String drawThreadInfo(List<ThreadVO> threads, int width, int height) {
TableElement table = new TableElement(1, 3, 2, 1, 1, 1, 1, 1, 1).overflow(Overflow.HIDDEN).rightCellPadding(1);
// Header
table.add(
new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add(
"ID",
"NAME",
"GROUP",
"PRIORITY",
"STATE",
"%CPU",
"TIME",
"INTERRUPTED",
"DAEMON"
)
);
for (ThreadVO thread : threads) {
Color color = colorMapping.get(thread.getState());
long seconds = thread.getTime();
long min = seconds / 60;
String time = min + ":" + (seconds % 60);
long cpu = thread.getCpu();
LabelElement daemonLabel = new LabelElement(thread.isDaemon());
if (!thread.isDaemon()) {
daemonLabel.setStyle(Style.style(Color.magenta));
}
table.row(
new LabelElement(thread.getId()),
new LabelElement(thread.getName()),
new LabelElement(thread.getGroup()),
new LabelElement(thread.getPriority()),
new LabelElement(thread.getState()).style(color.fg()),
new LabelElement(cpu),
new LabelElement(time),
new LabelElement(thread.isInterrupted()),
daemonLabel
);
}
return RenderUtil.render(table, width, height);
}
}
@@ -1,5 +1,6 @@
package com.taobao.arthas.core.util;
import com.taobao.arthas.core.command.model.BlockingLockInfo;
import com.taobao.arthas.core.view.Ansi;
import java.arthas.SpyAPI;
@@ -221,9 +222,9 @@ abstract public class ThreadUtil {
}
BlockingLockInfo blockingLockInfo = new BlockingLockInfo();
blockingLockInfo.threadInfo = ownerThreadPerLock.get(mostBlockingLock);
blockingLockInfo.lockIdentityHashCode = mostBlockingLock;
blockingLockInfo.blockingThreadCount = blockCountPerLock.get(mostBlockingLock);
blockingLockInfo.setThreadInfo(ownerThreadPerLock.get(mostBlockingLock));
blockingLockInfo.setLockIdentityHashCode(mostBlockingLock);
blockingLockInfo.setBlockingThreadCount(blockCountPerLock.get(mostBlockingLock));
return blockingLockInfo;
}
@@ -234,8 +235,8 @@ abstract public class ThreadUtil {
public static String getFullStacktrace(BlockingLockInfo blockingLockInfo) {
return getFullStacktrace(blockingLockInfo.threadInfo, -1, blockingLockInfo.lockIdentityHashCode,
blockingLockInfo.blockingThreadCount);
return getFullStacktrace(blockingLockInfo.getThreadInfo(), -1, blockingLockInfo.getLockIdentityHashCode(),
blockingLockInfo.getBlockingThreadCount());
}
@@ -329,17 +330,6 @@ abstract public class ThreadUtil {
return sb.toString().replace("\t", " ");
}
public static class BlockingLockInfo {
// the thread info that is holing this lock.
public ThreadInfo threadInfo = null;
// the associated LockInfo object
public int lockIdentityHashCode = 0;
// the number of thread that is blocked on this lock
public int blockingThreadCount = 0;
}
/**
* </pre>
* java.lang.Thread.getStackTrace(Thread.java:1559),