mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+134
-209
@@ -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;
|
||||
}
|
||||
}
|
||||
+22
-33
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user