transform command: dashboard, thread

pull/1306/head
gongdewei 5 years ago
parent 40c2c5e039
commit 5b7603eebe

@ -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,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,75 @@
package com.taobao.arthas.core.command.model;
/**
* Dashboard - Runtime
*
* @author gongdewei 2020/4/22
*/
public class RuntimeInfoVO {
private String osName;
private String osVersion;
private String javaVersion;
private String javaHome;
private double systemLoadAverage;
private int processors;
private long uptime;
public RuntimeInfoVO() {
}
public String getOsName() {
return osName;
}
public void setOsName(String osName) {
this.osName = osName;
}
public String getOsVersion() {
return osVersion;
}
public void setOsVersion(String osVersion) {
this.osVersion = osVersion;
}
public String getJavaVersion() {
return javaVersion;
}
public void setJavaVersion(String javaVersion) {
this.javaVersion = javaVersion;
}
public String getJavaHome() {
return javaHome;
}
public void setJavaHome(String javaHome) {
this.javaHome = javaHome;
}
public double getSystemLoadAverage() {
return systemLoadAverage;
}
public void setSystemLoadAverage(double systemLoadAverage) {
this.systemLoadAverage = systemLoadAverage;
}
public int getProcessors() {
return processors;
}
public void setProcessors(int processors) {
this.processors = processors;
}
public long getUptime() {
return uptime;
}
public void setUptime(long uptime) {
this.uptime = uptime;
}
}

@ -0,0 +1,36 @@
package com.taobao.arthas.core.command.model;
import java.lang.management.ThreadInfo;
/**
* ThreadInfo with cpuUsage
*
* @author gongdewei 2020/4/26
*/
public class ThreadCpuInfo {
private ThreadInfo threadInfo;
private long cpuUsage;
public ThreadCpuInfo(ThreadInfo threadInfo, long cpuUsage) {
this.threadInfo = threadInfo;
this.cpuUsage = cpuUsage;
}
public long getCpuUsage() {
return cpuUsage;
}
public void setCpuUsage(long cpuUsage) {
this.cpuUsage = cpuUsage;
}
public ThreadInfo threadInfo() {
return threadInfo;
}
public void setThreadInfo(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
}

@ -0,0 +1,91 @@
package com.taobao.arthas.core.command.model;
import java.lang.management.ThreadInfo;
import java.util.List;
import java.util.Map;
/**
* Model of 'thread' command
*
* @author gongdewei 2020/4/26
*/
public class ThreadModel extends ResultModel {
//single thread: thread 12
private ThreadInfo threadInfo;
//thread -b
private BlockingLockInfo blockingLockInfo;
//thread -n 5
private ThreadCpuInfo[] busyThreads;
//thread stats
private List<ThreadVO> threadStats;
private Map<Thread.State, Integer> threadStateCount;
public ThreadModel() {
}
public ThreadModel(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
public ThreadModel(BlockingLockInfo blockingLockInfo) {
this.blockingLockInfo = blockingLockInfo;
}
public ThreadModel(ThreadCpuInfo[] busyThreads) {
this.busyThreads = busyThreads;
}
public ThreadModel(List<ThreadVO> threadStats, Map<Thread.State, Integer> threadStateCount) {
this.threadStats = threadStats;
this.threadStateCount = threadStateCount;
}
@Override
public String getType() {
return "thread";
}
public ThreadInfo getThreadInfo() {
return threadInfo;
}
public void setThreadInfo(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
public BlockingLockInfo getBlockingLockInfo() {
return blockingLockInfo;
}
public void setBlockingLockInfo(BlockingLockInfo blockingLockInfo) {
this.blockingLockInfo = blockingLockInfo;
}
public ThreadCpuInfo[] getBusyThreads() {
return busyThreads;
}
public void setBusyThreads(ThreadCpuInfo[] busyThreads) {
this.busyThreads = busyThreads;
}
public List<ThreadVO> getThreadStats() {
return threadStats;
}
public void setThreadStats(List<ThreadVO> threadStats) {
this.threadStats = threadStats;
}
public Map<Thread.State, Integer> getThreadStateCount() {
return threadStateCount;
}
public void setThreadStateCount(Map<Thread.State, Integer> threadStateCount) {
this.threadStateCount = threadStateCount;
}
}

@ -0,0 +1,95 @@
package com.taobao.arthas.core.command.model;
import java.lang.Thread.State;
/**
* Thread VO of 'dashboard' and 'thread' command
*
* @author gongdewei 2020/4/22
*/
public class ThreadVO {
private long id;
private String name;
private String group;
private int priority;
private State state;
private long cpu;
private long time;
private boolean interrupted;
private boolean daemon;
public ThreadVO() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public long getCpu() {
return cpu;
}
public void setCpu(long cpu) {
this.cpu = cpu;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public boolean isInterrupted() {
return interrupted;
}
public void setInterrupted(boolean interrupted) {
this.interrupted = interrupted;
}
public boolean isDaemon() {
return daemon;
}
public void setDaemon(boolean daemon) {
this.daemon = daemon;
}
}

@ -0,0 +1,129 @@
package com.taobao.arthas.core.command.model;
import java.util.List;
/**
* Tomcat info of 'dashboard' command
*
* @author gongdewei 2020/4/23
*/
public class TomcatInfoVO {
private List<ConnectorStats> connectorStats;
private List<ThreadPool> threadPools;
public TomcatInfoVO() {
}
public List<ConnectorStats> getConnectorStats() {
return connectorStats;
}
public void setConnectorStats(List<ConnectorStats> connectorStats) {
this.connectorStats = connectorStats;
}
public List<ThreadPool> getThreadPools() {
return threadPools;
}
public void setThreadPools(List<ThreadPool> threadPools) {
this.threadPools = threadPools;
}
public static class ConnectorStats {
private String name;
private double qps;
private double rt;
private double error;
private long received;
private long sent;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getQps() {
return qps;
}
public void setQps(double qps) {
this.qps = qps;
}
public double getRt() {
return rt;
}
public void setRt(double rt) {
this.rt = rt;
}
public double getError() {
return error;
}
public void setError(double error) {
this.error = error;
}
public long getReceived() {
return received;
}
public void setReceived(long received) {
this.received = received;
}
public long getSent() {
return sent;
}
public void setSent(long sent) {
this.sent = sent;
}
}
public static class ThreadPool {
private String name;
private long busy;
private long total;
public ThreadPool() {
}
public ThreadPool(String name, long busy, long total) {
this.name = name;
this.busy = busy;
this.total = total;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getBusy() {
return busy;
}
public void setBusy(long busy) {
this.busy = busy;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
}
}

@ -5,6 +5,11 @@ import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.taobao.arthas.core.command.Constants;
import com.taobao.arthas.core.command.model.DashboardModel;
import com.taobao.arthas.core.command.model.GcInfoVO;
import com.taobao.arthas.core.command.model.MemoryEntryVO;
import com.taobao.arthas.core.command.model.RuntimeInfoVO;
import com.taobao.arthas.core.command.model.TomcatInfoVO;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.arthas.core.shell.handlers.Handler;
@ -18,13 +23,6 @@ import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import com.taobao.text.Color;
import com.taobao.text.Decoration;
import com.taobao.text.Style;
import com.taobao.text.renderers.ThreadRenderer;
import com.taobao.text.ui.RowElement;
import com.taobao.text.ui.TableElement;
import com.taobao.text.util.RenderUtil;
import java.lang.management.BufferPoolMXBean;
import java.lang.management.GarbageCollectorMXBean;
@ -32,11 +30,17 @@ import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_BUFFER_POOL;
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_HEAP;
import static com.taobao.arthas.core.command.model.MemoryEntryVO.TYPE_NON_HEAP;
/**
* @author hengyunabc 20151119 11:57:21
*/
@ -140,93 +144,101 @@ public class DashboardCommand extends AnnotatedCommand {
return name.replace(' ', '_').toLowerCase();
}
private static void addBufferPoolMemoryInfo(TableElement table) {
try {
@SuppressWarnings("rawtypes")
Class bufferPoolMXBeanClass = Class.forName("java.lang.management.BufferPoolMXBean");
@SuppressWarnings("unchecked")
List<BufferPoolMXBean> bufferPoolMXBeans = ManagementFactory.getPlatformMXBeans(bufferPoolMXBeanClass);
for (BufferPoolMXBean mbean : bufferPoolMXBeans) {
long used = mbean.getMemoryUsed();
long total = mbean.getTotalCapacity();
new MemoryEntry(mbean.getName(), used, total, Long.MIN_VALUE).addTableRow(table);
}
} 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 addMemoryInfo(TableElement table) {
MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
private static void addMemoryInfo(DashboardModel dashboardModel) {
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
Map<String, List<MemoryEntryVO>> memoryInfoMap = new LinkedHashMap<String, List<MemoryEntryVO>>();
dashboardModel.setMemoryInfo(memoryInfoMap);
new MemoryEntry("heap", heapMemoryUsage).addTableRow(table, Decoration.bold.bold());
//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());
new MemoryEntry(poolName, usage).addTableRow(table);
heapMemEntries.add(createMemoryEntryVO(TYPE_HEAP, poolName, usage));
}
}
memoryInfoMap.put(TYPE_HEAP, heapMemEntries);
new MemoryEntry("nonheap", nonHeapMemoryUsage).addTableRow(table, Decoration.bold.bold());
//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());
new MemoryEntry(poolName, usage).addTableRow(table);
nonheapMemEntries.add(createMemoryEntryVO(TYPE_NON_HEAP, poolName, usage));
}
}
memoryInfoMap.put(TYPE_NON_HEAP, nonheapMemEntries);
addBufferPoolMemoryInfo(table);
addBufferPoolMemoryInfo(memoryInfoMap);
}
private static void addGcInfo(TableElement table) {
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());
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")
List<BufferPoolMXBean> bufferPoolMXBeans = ManagementFactory.getPlatformMXBeans(bufferPoolMXBeanClass);
for (BufferPoolMXBean mbean : bufferPoolMXBeans) {
long used = mbean.getMemoryUsed();
long total = mbean.getTotalCapacity();
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 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 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 MemoryEntryVO createMemoryEntryVO(String type, String name, MemoryUsage memoryUsage) {
return new MemoryEntryVO(type, name, memoryUsage.getUsed(), memoryUsage.getCommitted(), memoryUsage.getMax());
}
private static void addGcInfo(DashboardModel dashboardModel) {
List<GcInfoVO> gcInfos = new ArrayList<GcInfoVO>();
dashboardModel.setGcInfos(gcInfos);
return String.format("%d%s", size / unit, unitStr);
List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcMXBean : garbageCollectorMxBeans) {
String name = gcMXBean.getName();
gcInfos.add(new GcInfoVO(beautifyName(name), gcMXBean.getCollectionCount(), gcMXBean.getCollectionTime()));
}
}
private void addTomcatInfo(TableElement table) {
private void addTomcatInfo(DashboardModel dashboardModel) {
// 如果请求tomcat信息失败则不显示tomcat信息
if (!NetUtils.request("http://localhost:8006").isSuccess()) {
return;
}
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()));
int width = process.width();
int height = process.height();
//memory
addMemoryInfo(dashboardModel);
// 上半部分放thread top。下半部分再切分为田字格其中上面两格放memory, gc的信息。下面两格放tomcat,
// runtime的信息
int totalHeight = height - 1;
int threadTopHeight = totalHeight / 2;
int lowerHalf = totalHeight - threadTopHeight;
//gc
addGcInfo(dashboardModel);
int runtimeInfoHeight = lowerHalf / 2;
int heapInfoHeight = lowerHalf - runtimeInfoHeight;
//runtime
addRuntimeInfo(dashboardModel);
String threadInfo = drawThreadInfo(width, threadTopHeight);
String memoryAndGc = drawMemoryInfoAndGcInfo(width, runtimeInfoHeight);
String runTimeAndTomcat = drawRuntimeInfoAndTomcatInfo(width, heapInfoHeight);
//tomcat
addTomcatInfo(dashboardModel);
process.write(threadInfo + memoryAndGc + runTimeAndTomcat);
process.appendResult(dashboardModel);
count++;
process.times().incrementAndGet();
count++;
process.times().incrementAndGet();
} catch (Throwable e) {
String msg = "process dashboard failed: " + e.getMessage();
logger.error(msg, e);
process.end(-1, msg);
}
}
}
}

@ -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;
}
private int processBlockingThread(CommandProcess process) {
int status = 0;
ThreadUtil.BlockingLockInfo blockingLockInfo = ThreadUtil.findMostBlockingLock();
//thread stats
ThreadSampler threadSampler = new ThreadSampler();
threadSampler.setSampleInterval(sampleInterval);
List<ThreadVO> threadStats = threadSampler.sample(resultThreads);
if (blockingLockInfo.threadInfo == null) {
process.write("No most blocking thread found!\n");
status = 1;
} else {
String stacktrace = ThreadUtil.getFullStacktrace(blockingLockInfo);
process.write(stacktrace);
process.appendResult(new ThreadModel(threadStats, stateCountMap));
return ExitStatus.success();
}
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,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),

Loading…
Cancel
Save